mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-09-12 16:40:40 +08:00
refactor: 移除启动页逻辑,新增全局组件与偏好设置系统
1. 移除旧版启动页分流逻辑,直接跳转首页 2. 新增返回顶部、章节标题、导航栏等全局组件 3. 重构偏好设置系统,实现站点默认与本地差异分层管理 4. 删除冗余的测试mock、插件模块与样式文件 5. 优化文章卡片与评论组件样式,更新全局主题色 6. 清理废弃的请求参数与配置项
This commit is contained in:
+284
-206
@@ -1,260 +1,338 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 应用设置页(源自旧项目 pagesA/setting,新建复刻)
|
||||
* 布局设置(首页布局/文章卡片样式)+ 功能设置(瀑布流/友链简洁/圆头像/轮播指示器)+ 保存/恢复默认
|
||||
* 偏好设置页(两层:站点默认 L0 + 本地差异 L1-L,设计见 .docs/config-system-v2-redesign §3-4)
|
||||
* - 每项可「跟随站点默认」(差异为空)或覆盖为具体值;改动即写本地差异(uh_pref_local_v1)即时生效;
|
||||
* - 底部「重置为站点默认」= 清空全部本地差异,回退站长在插件后台配置的默认(未配置则为内置默认)。
|
||||
*/
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { DefaultAppSettings } from '@/config/appSettings'
|
||||
import { useAppConfigStore } from '@/store/appConfig'
|
||||
import { useSettingStore } from '@/store/setting'
|
||||
import type { IAppSettings } from '@/config/appSettings'
|
||||
import { collectSiteDefaults, isLocalOverride, readLocalPrefs } from '@/utils/preference'
|
||||
import type { LocalPrefs } from '@/utils/preference'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '应用设置',
|
||||
navigationBarTitleText: '偏好设置',
|
||||
},
|
||||
})
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const appConfigStore = useAppConfigStore()
|
||||
|
||||
/* ---------------- 状态 ---------------- */
|
||||
const loading = ref(true)
|
||||
const isSaved = ref(true)
|
||||
const firstLoad = ref(true)
|
||||
|
||||
/** 本地编辑副本(不与 store 直接双向绑定,保存时提交) */
|
||||
const appSettings = reactive<IAppSettings>(JSON.parse(JSON.stringify(DefaultAppSettings)))
|
||||
|
||||
/* ---------------- 选择器配置 ---------------- */
|
||||
const homeLayout = reactive({
|
||||
list: [
|
||||
{ name: '一行一列', value: 'h_row_col1' },
|
||||
{ name: '一行两列', value: 'h_row_col2' },
|
||||
],
|
||||
selectLabel: '一行一列',
|
||||
selectValue: 'h_row_col1',
|
||||
/** 确保启动合并已执行(入口页未跑或 H5 直达时兜底) */
|
||||
onLoad(() => {
|
||||
if (!settingStore.siteDefaults) {
|
||||
settingStore.applySiteDefaults(collectSiteDefaults(appConfigStore.configs))
|
||||
}
|
||||
uni.setNavigationBarTitle({ title: '偏好设置' })
|
||||
})
|
||||
|
||||
const articleCardStyle = reactive({
|
||||
list: [
|
||||
{ name: '左图右文', value: 'lr_image_text' },
|
||||
{ name: '左文右图', value: 'lr_text_image' },
|
||||
{ name: '上图下文', value: 'tb_image_text' },
|
||||
{ name: '上文下图', value: 'tb_text_image' },
|
||||
{ name: '只有文字', value: 'only_text' },
|
||||
],
|
||||
selectLabel: '左图右文',
|
||||
selectValue: 'lr_image_text',
|
||||
})
|
||||
/* ---------------- 路径取值工具 ---------------- */
|
||||
type Path = string[]
|
||||
|
||||
const dotPositionList = reactive([
|
||||
{ name: '右边', value: 'right', checked: true },
|
||||
{ name: '下边', value: 'bottom', checked: false },
|
||||
])
|
||||
|
||||
/* ---------------- 工具 ---------------- */
|
||||
function handleFindObjInList<T extends Record<string, unknown>>(list: T[], key: string, value: unknown): T {
|
||||
return list.find(x => x[key] === value) || list[0]
|
||||
function getByPath(obj: unknown, path: Path): unknown {
|
||||
let cursor: unknown = obj
|
||||
for (const key of path) {
|
||||
if (cursor === null || cursor === undefined)
|
||||
return undefined
|
||||
cursor = (cursor as Record<string, unknown>)[key]
|
||||
}
|
||||
return cursor
|
||||
}
|
||||
|
||||
/** 统一处理选择框回显 */
|
||||
function handleHandleFormatSelect() {
|
||||
const _homeLayout = handleFindObjInList(homeLayout.list, 'value', appSettings.layout.home)
|
||||
homeLayout.selectLabel = _homeLayout.name
|
||||
homeLayout.selectValue = _homeLayout.value
|
||||
/** 按路径构造差异 patch(null 表示删除该键=跟随站点默认) */
|
||||
function buildPatch(path: Path, value: unknown): LocalPrefs {
|
||||
const [head, ...rest] = path
|
||||
if (rest.length === 0)
|
||||
return { [head]: value } as LocalPrefs
|
||||
return { [head]: buildPatch(rest, value) } as LocalPrefs
|
||||
}
|
||||
|
||||
const _cardStyle = handleFindObjInList(articleCardStyle.list, 'value', appSettings.layout.cardType)
|
||||
articleCardStyle.selectLabel = _cardStyle.name
|
||||
articleCardStyle.selectValue = _cardStyle.value
|
||||
/** 偏好字段定义(现页已有项;弹幕已下线、友链分组二期再开) */
|
||||
interface PrefDef {
|
||||
key: string
|
||||
label: string
|
||||
kind: 'bool' | 'enum'
|
||||
path: Path
|
||||
options?: { label: string, value: string }[]
|
||||
siteLabelOf?: (value: string) => string
|
||||
}
|
||||
|
||||
const _dot = handleFindObjInList(dotPositionList, 'value', appSettings.banner.dotPosition)
|
||||
dotPositionList.forEach((item) => {
|
||||
item.checked = item.value === _dot.value
|
||||
})
|
||||
const layoutPrefs: PrefDef[] = [
|
||||
{
|
||||
key: 'home',
|
||||
label: '首页文章布局',
|
||||
kind: 'enum',
|
||||
path: ['layout', 'home'],
|
||||
options: [
|
||||
{ label: '一行一列', value: 'h_row_col1' },
|
||||
{ label: '一行两列', value: 'h_row_col2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'cardType',
|
||||
label: '文章卡片样式',
|
||||
kind: 'enum',
|
||||
path: ['layout', 'cardType'],
|
||||
options: [
|
||||
{ label: '左图右文', value: 'lr_image_text' },
|
||||
{ label: '左文右图', value: 'lr_text_image' },
|
||||
{ label: '上图下文', value: 'tb_image_text' },
|
||||
{ label: '上文下图', value: 'tb_text_image' },
|
||||
{ label: '只有文字', value: 'only_text' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const featurePrefs: PrefDef[] = [
|
||||
{ key: 'useWaterfull', label: '图库瀑布流模式', kind: 'bool', path: ['gallery', 'useWaterfull'] },
|
||||
{ key: 'useSimple', label: '友链简洁模式', kind: 'bool', path: ['links', 'useSimple'] },
|
||||
{ key: 'isAvatarRadius', label: '是否圆形头像', kind: 'bool', path: ['isAvatarRadius'] },
|
||||
{ key: 'useDot', label: '轮播图指示器', kind: 'bool', path: ['banner', 'useDot'] },
|
||||
{
|
||||
key: 'dotPosition',
|
||||
label: '指示器位置',
|
||||
kind: 'enum',
|
||||
path: ['banner', 'dotPosition'],
|
||||
options: [
|
||||
{ label: '右边', value: 'right' },
|
||||
{ label: '下边', value: 'bottom' },
|
||||
],
|
||||
siteLabelOf: (value: string) => {
|
||||
const map: Record<string, string> = { right: '右边', bottom: '下边', left: '左边', top: '上边' }
|
||||
return map[value] || value
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
/* ---------------- 状态读取 ---------------- */
|
||||
function valueOf(path: Path): unknown {
|
||||
return getByPath(settingStore.settings, path)
|
||||
}
|
||||
|
||||
/** 站点默认值(未配置时回退内置默认) */
|
||||
function siteDefaultOf(path: Path): unknown {
|
||||
const site = getByPath(settingStore.siteDefaults, path)
|
||||
if (site !== undefined && site !== null)
|
||||
return site
|
||||
return getByPath(DefaultAppSettings, path)
|
||||
}
|
||||
|
||||
function isOverridden(path: Path): boolean {
|
||||
return isLocalOverride(readLocalPrefs(), path)
|
||||
}
|
||||
|
||||
function enumLabelOf(def: PrefDef, value: unknown): string {
|
||||
const hit = def.options?.find(opt => opt.value === value)
|
||||
if (hit)
|
||||
return hit.label
|
||||
if (def.siteLabelOf && typeof value === 'string')
|
||||
return def.siteLabelOf(value)
|
||||
return value === undefined || value === null ? '—' : String(value)
|
||||
}
|
||||
|
||||
/* ---------------- 交互 ---------------- */
|
||||
function handleOnHomeLayoutConfirm() {
|
||||
const _select = handleFindObjInList(homeLayout.list, 'value', appSettings.layout.home)
|
||||
homeLayout.selectLabel = _select.name
|
||||
homeLayout.selectValue = _select.value
|
||||
/** 开关事件(模板透传 $event) */
|
||||
function handleSwitchChange(def: PrefDef, detail: { value?: unknown }) {
|
||||
handleBoolChange(def.path, detail.value === true)
|
||||
}
|
||||
|
||||
function handleOnArticleCardStyleConfirm() {
|
||||
const _select = handleFindObjInList(articleCardStyle.list, 'value', appSettings.layout.cardType)
|
||||
articleCardStyle.selectLabel = _select.name
|
||||
articleCardStyle.selectValue = _select.value
|
||||
}
|
||||
|
||||
function handleOnBannerDotChange(e: { value?: string }) {
|
||||
const value = e.value || 'right'
|
||||
appSettings.banner.dotPosition = value
|
||||
}
|
||||
|
||||
/** 保存设置 */
|
||||
function handleOnSave() {
|
||||
isSaved.value = true
|
||||
settingStore.settings = JSON.parse(JSON.stringify(appSettings))
|
||||
uni.showToast({ icon: 'none', title: '保存成功,部分设置在重启后生效!' })
|
||||
}
|
||||
|
||||
/** 恢复默认设置 */
|
||||
function handleOnSaveDefault() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '您确定要恢复为默认的设置吗?',
|
||||
showCancel: true,
|
||||
cancelText: '否',
|
||||
cancelColor: '#999999',
|
||||
confirmText: '是',
|
||||
confirmColor: '#03a9f4',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
isSaved.value = true
|
||||
settingStore.updateDefaultAppSettings()
|
||||
Object.assign(appSettings, JSON.parse(JSON.stringify(DefaultAppSettings)))
|
||||
handleHandleFormatSelect()
|
||||
uni.showToast({ icon: 'none', title: '系统设置已恢复为默认配置,部分设置在重启后生效!' })
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function handleOnBack() {
|
||||
if (isSaved.value) {
|
||||
uni.navigateBack()
|
||||
return
|
||||
}
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '您当前可能有未保存的数据,确定返回吗?',
|
||||
showCancel: true,
|
||||
cancelText: '否',
|
||||
cancelColor: '#999999',
|
||||
confirmText: '是',
|
||||
confirmColor: '#03a9f4',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateBack()
|
||||
isSaved.value = true
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/* ---------------- 监听 ---------------- */
|
||||
watch(appSettings, () => {
|
||||
if (firstLoad.value) {
|
||||
firstLoad.value = false
|
||||
/** 开关类:选值等于站点默认则还原为跟随(只存差异) */
|
||||
function handleBoolChange(path: Path, next: boolean) {
|
||||
if (next === siteDefaultOf(path)) {
|
||||
settingStore.savePreference(buildPatch(path, null))
|
||||
}
|
||||
else {
|
||||
isSaved.value = false
|
||||
settingStore.savePreference(buildPatch(path, next))
|
||||
}
|
||||
}, { deep: true })
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
uni.setNavigationBarTitle({ title: '应用设置' })
|
||||
Object.assign(appSettings, JSON.parse(JSON.stringify(settingStore.settings)))
|
||||
handleHandleFormatSelect()
|
||||
uni.showLoading({ title: '加载中...', mask: true })
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
uni.hideLoading()
|
||||
}, 500)
|
||||
})
|
||||
/** 单项还原为跟随站点默认 */
|
||||
function handleRevert(path: Path) {
|
||||
settingStore.savePreference(buildPatch(path, null))
|
||||
}
|
||||
|
||||
/* ---------------- 枚举底部弹层 ---------------- */
|
||||
const enumSheet = ref<{ show: boolean, def: PrefDef | null }>({ show: false, def: null })
|
||||
|
||||
function handleOpenEnum(def: PrefDef) {
|
||||
enumSheet.value = { show: true, def }
|
||||
}
|
||||
|
||||
function handleCloseEnum() {
|
||||
enumSheet.value.show = false
|
||||
}
|
||||
|
||||
function handleChooseEnum(value: string | null) {
|
||||
const def = enumSheet.value.def
|
||||
if (def) {
|
||||
if (value === null || value === siteDefaultOf(def.path)) {
|
||||
handleRevert(def.path)
|
||||
}
|
||||
else {
|
||||
settingStore.savePreference(buildPatch(def.path, value))
|
||||
}
|
||||
}
|
||||
handleCloseEnum()
|
||||
}
|
||||
|
||||
/** 当前枚举项是否处于「跟随站点默认」 */
|
||||
function isFollowing(def: PrefDef): boolean {
|
||||
return !isOverridden(def.path)
|
||||
}
|
||||
|
||||
/* ---------------- 重置全部 ---------------- */
|
||||
function handleResetAll() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定将所有偏好恢复为站点默认吗?本地自定义的偏好将被清除,未配置站点默认的项将恢复为内置默认。',
|
||||
showCancel: true,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
confirmColor: '#03a9f4',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
settingStore.resetPreferences()
|
||||
enumSheet.value.show = false
|
||||
uni.showToast({ icon: 'none', title: '已恢复为站点默认' })
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="app-page box-border min-h-screen pb-[140rpx]" style="background-color: #fafafd;">
|
||||
<view v-if="!loading">
|
||||
<!-- 布局设置 -->
|
||||
<view class="setting-sheet mx-6 mt-6 overflow-hidden rounded-xl bg-white shadow-sm">
|
||||
<view class="sheet-title border-b-2 border-[#f5f5f5] px-6 py-1.5">
|
||||
<text class="title-text text-[30rpx] text-[#303133] font-bold">布局</text>
|
||||
<text class="title-desc ml-3 text-[22rpx] text-[#999]">应用以及文章列表布局设置</text>
|
||||
</view>
|
||||
<view class="sheet-content">
|
||||
<!-- 首页布局 -->
|
||||
<view class="pick-row flex items-center justify-between border-b-2 border-[#f5f5f5] px-8 py-7" @click="handleOnHomeLayoutConfirm">
|
||||
<text class="row-label text-[28rpx] text-[#333]">首页文章布局</text>
|
||||
<view class="row-value flex items-center gap-2">
|
||||
<text class="value-text text-[26rpx] text-[#999]">{{ homeLayout.selectLabel }}</text>
|
||||
<wd-icon name="arrow-right" size="12px" color="#999" />
|
||||
</view>
|
||||
<view class="app-page box-border min-h-screen pb-[180rpx]" style="background-color: #fafafd;">
|
||||
<!-- 说明 -->
|
||||
<view class="pref-tip mx-6 mt-6 rounded-xl bg-white px-6 py-4 shadow-sm">
|
||||
<text class="tip-text text-[22rpx] leading-[1.6] text-[#909399]">
|
||||
你的偏好仅保存在本机。未自定义的项自动跟随站长在插件后台配置的「站点默认」;点击底部「重置为站点默认」可清空全部本地偏好。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 布局设置 -->
|
||||
<view class="setting-sheet mx-6 mt-6 overflow-hidden rounded-xl bg-white shadow-sm">
|
||||
<view class="sheet-title border-b-2 border-[#f5f5f5] px-6 py-1.5">
|
||||
<text class="title-text text-[30rpx] text-[#303133] font-bold">布局</text>
|
||||
<text class="title-desc ml-3 text-[22rpx] text-[#999]">应用以及文章列表布局设置</text>
|
||||
</view>
|
||||
<view class="sheet-content">
|
||||
<view
|
||||
v-for="def in layoutPrefs"
|
||||
:key="def.key"
|
||||
class="pick-row flex items-center justify-between border-b-2 border-[#f5f5f5] px-8 py-7"
|
||||
@click="handleOpenEnum(def)"
|
||||
>
|
||||
<view class="row-left flex flex-col">
|
||||
<text class="row-label text-[28rpx] text-[#333]">{{ def.label }}</text>
|
||||
<text v-if="isFollowing(def)" class="row-sub mt-1 text-[20rpx] text-[#b2b6bd]">跟随站点默认</text>
|
||||
<text v-else class="row-sub mt-1 text-[20rpx] text-[#03a9f4]">已自定义</text>
|
||||
</view>
|
||||
<!-- 文章卡片样式 -->
|
||||
<view class="pick-row flex items-center justify-between px-8 py-7" @click="handleOnArticleCardStyleConfirm">
|
||||
<text class="row-label text-[28rpx] text-[#333]">文章卡片样式</text>
|
||||
<view class="row-value flex items-center gap-2">
|
||||
<text class="value-text text-[26rpx] text-[#999]">{{ articleCardStyle.selectLabel }}</text>
|
||||
<wd-icon name="arrow-right" size="12px" color="#999" />
|
||||
</view>
|
||||
<view class="row-value flex items-center gap-2">
|
||||
<text class="value-text text-[26rpx] text-[#999]">{{ enumLabelOf(def, valueOf(def.path)) }}</text>
|
||||
<wd-icon name="arrow-right" size="12px" color="#999" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能设置 -->
|
||||
<view class="setting-sheet mx-6 mt-6 overflow-hidden rounded-xl bg-white shadow-sm">
|
||||
<view class="sheet-title border-b-2 border-[#f5f5f5] px-6 py-1.5">
|
||||
<text class="title-text text-[30rpx] text-[#303133] font-bold">功能</text>
|
||||
<text class="title-desc ml-3 text-[22rpx] text-[#999]">一些常用的功能性设置</text>
|
||||
</view>
|
||||
<view class="sheet-content">
|
||||
<view class="switch-row flex items-center justify-between border-b-2 border-[#f5f5f5] px-8 py-7">
|
||||
<text class="row-label text-[28rpx] text-[#333]">图库瀑布流模式</text>
|
||||
<wd-switch v-model="appSettings.gallery.useWaterfull" />
|
||||
</view>
|
||||
<view class="switch-row flex items-center justify-between border-b-2 border-[#f5f5f5] px-8 py-7">
|
||||
<text class="row-label text-[28rpx] text-[#333]">友链简洁模式</text>
|
||||
<wd-switch v-model="appSettings.links.useSimple" />
|
||||
</view>
|
||||
<view class="switch-row flex items-center justify-between border-b-2 border-[#f5f5f5] px-8 py-7">
|
||||
<text class="row-label text-[28rpx] text-[#333]">是否圆形头像</text>
|
||||
<wd-switch v-model="appSettings.isAvatarRadius" />
|
||||
</view>
|
||||
<view class="switch-row flex items-center justify-between border-b-2 border-[#f5f5f5] px-8 py-7">
|
||||
<text class="row-label text-[28rpx] text-[#333]">轮播图指示器</text>
|
||||
<wd-switch v-model="appSettings.banner.useDot" />
|
||||
</view>
|
||||
<!-- 指示器位置 -->
|
||||
<view v-if="appSettings.banner.useDot" class="switch-row flex items-center justify-between px-8 py-7">
|
||||
<text class="row-label text-[28rpx] text-[#333]">指示器位置</text>
|
||||
<view class="radio-group flex gap-4">
|
||||
<view
|
||||
v-for="item in dotPositionList"
|
||||
:key="item.value"
|
||||
class="radio-item rounded-3xl px-6 py-1 text-[24rpx] text-[#999]"
|
||||
:class="item.checked ? 'bg-[#03a9f4] text-white' : 'bg-[#f5f5f5]'"
|
||||
@click="item.checked = true; handleOnBannerDotChange({ value: item.value })"
|
||||
>
|
||||
{{ item.name }}
|
||||
<!-- 功能设置 -->
|
||||
<view class="setting-sheet mx-6 mt-6 overflow-hidden rounded-xl bg-white shadow-sm">
|
||||
<view class="sheet-title border-b-2 border-[#f5f5f5] px-6 py-1.5">
|
||||
<text class="title-text text-[30rpx] text-[#303133] font-bold">功能</text>
|
||||
<text class="title-desc ml-3 text-[22rpx] text-[#999]">一些常用的功能性设置</text>
|
||||
</view>
|
||||
<view class="sheet-content">
|
||||
<template v-for="def in featurePrefs" :key="def.key">
|
||||
<!-- 布尔开关 -->
|
||||
<view v-if="def.kind === 'bool'" class="switch-row flex items-center justify-between border-b-2 border-[#f5f5f5] px-8 py-5">
|
||||
<view class="row-left flex flex-col">
|
||||
<text class="row-label text-[28rpx] text-[#333]">{{ def.label }}</text>
|
||||
<view class="mt-1 flex items-center gap-2">
|
||||
<text v-if="isFollowing(def)" class="row-sub text-[20rpx] text-[#b2b6bd]">跟随站点默认</text>
|
||||
<text v-else class="row-sub text-[20rpx] text-[#03a9f4]">已自定义</text>
|
||||
<text
|
||||
v-if="!isFollowing(def)"
|
||||
class="revert-text text-[20rpx] text-[#909399] underline"
|
||||
@click.stop="handleRevert(def.path)"
|
||||
>跟随默认</text>
|
||||
</view>
|
||||
</view>
|
||||
<wd-switch :model-value="!!valueOf(def.path)" @change="handleSwitchChange(def, $event)" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 枚举选择(指示器位置) -->
|
||||
<view v-else class="pick-row flex items-center justify-between px-8 py-5" @click="handleOpenEnum(def)">
|
||||
<view class="row-left flex flex-col">
|
||||
<text class="row-label text-[28rpx] text-[#333]">{{ def.label }}</text>
|
||||
<view class="mt-1 flex items-center gap-2">
|
||||
<text v-if="isFollowing(def)" class="row-sub text-[20rpx] text-[#b2b6bd]">跟随站点默认</text>
|
||||
<text v-else class="row-sub text-[20rpx] text-[#03a9f4]">已自定义</text>
|
||||
<text
|
||||
v-if="!isFollowing(def)"
|
||||
class="revert-text text-[20rpx] text-[#909399] underline"
|
||||
@click.stop="handleRevert(def.path)"
|
||||
>跟随默认</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="row-value flex items-center gap-2">
|
||||
<text class="value-text text-[26rpx] text-[#999]">{{ enumLabelOf(def, valueOf(def.path)) }}</text>
|
||||
<wd-icon name="arrow-right" size="12px" color="#999" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作区域 -->
|
||||
<view v-if="!loading" class="btn-bar fixed bottom-0 left-0 box-border w-screen flex gap-6 bg-white p-6 shadow-sm">
|
||||
<wd-button type="primary" size="medium" @click="handleOnSave">
|
||||
保存设置
|
||||
</wd-button>
|
||||
<wd-button type="danger" size="medium" @click="handleOnSaveDefault">
|
||||
恢复默认设置
|
||||
</wd-button>
|
||||
<wd-button plain size="medium" @click="handleOnBack">
|
||||
返回
|
||||
<view class="btn-bar fixed bottom-0 left-0 box-border w-screen bg-white p-6 shadow-sm">
|
||||
<wd-button type="danger" size="medium" block @click="handleResetAll">
|
||||
重置为站点默认
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<!-- 枚举选择底部弹层 -->
|
||||
<wd-popup
|
||||
v-model="enumSheet.show"
|
||||
position="bottom"
|
||||
closable
|
||||
custom-style="border-radius: 24rpx 24rpx 0 0;"
|
||||
@close="handleCloseEnum"
|
||||
>
|
||||
<view v-if="enumSheet.def" class="enum-sheet box-border w-full pb-[env(safe-area-inset-bottom)]">
|
||||
<view class="enum-title border-b border-[#f5f5f5] py-6 text-center text-[30rpx] font-bold text-[#303133]">
|
||||
{{ enumSheet.def.label }}
|
||||
</view>
|
||||
<view
|
||||
class="enum-item flex items-center justify-between px-8 py-6"
|
||||
:class="isFollowing(enumSheet.def) ? 'text-[#03a9f4]' : 'text-[#333]'"
|
||||
@click="handleChooseEnum(null)"
|
||||
>
|
||||
<text class="text-[28rpx]">跟随站点默认</text>
|
||||
<wd-icon v-if="isFollowing(enumSheet.def)" name="check" size="16px" color="#03a9f4" />
|
||||
</view>
|
||||
<view
|
||||
v-for="opt in enumSheet.def.options"
|
||||
:key="opt.value"
|
||||
class="enum-item flex items-center justify-between border-t border-[#f5f5f5] px-8 py-6"
|
||||
:class="valueOf(enumSheet.def.path) === opt.value && !isFollowing(enumSheet.def) ? 'text-[#03a9f4]' : 'text-[#333]'"
|
||||
@click="handleChooseEnum(opt.value)"
|
||||
>
|
||||
<text class="text-[28rpx]">{{ opt.label }}</text>
|
||||
<wd-icon
|
||||
v-if="valueOf(enumSheet.def.path) === opt.value && !isFollowing(enumSheet.def)"
|
||||
name="check"
|
||||
size="16px"
|
||||
color="#03a9f4"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.app-page {
|
||||
/* 布局全部由 UnoCSS 原子类实现 */
|
||||
}
|
||||
|
||||
.btn-bar {
|
||||
:deep(wd-button) {
|
||||
flex: 1;
|
||||
|
||||
Reference in New Issue
Block a user