fix(admin): harden hero/banner round-trip and operator cross-links
Align enabled-meta semantics with frontend, allow truly empty titles on update, use router navigation for manager deep-links, and document fixed skeleton ownership in module schema copy.
This commit is contained in:
@@ -2,11 +2,16 @@
|
||||
<div class="aksrt-page-container content-page">
|
||||
<PageHeader
|
||||
title="Banner 管理"
|
||||
desc="管理首页 Banner 数据,供固定首页和相关展示区域调用;可控制是否显示文字。"
|
||||
desc="管理首页内容流 Banner 幻灯片(图片、文案、跳转、启用状态)。"
|
||||
/>
|
||||
|
||||
<n-card class="aksrt-section-card" style="margin-bottom:16px;">
|
||||
<template #header><strong>Banner 显示开关</strong></template>
|
||||
<template #header>
|
||||
<div class="aksrt-card-header-row">
|
||||
<strong>Banner 显示开关</strong>
|
||||
<n-button size="small" secondary @click="goHeaderComponents">顶部多功能组件(轮播参数)</n-button>
|
||||
</div>
|
||||
</template>
|
||||
<n-form label-placement="left" label-width="180" class="aksrt-form">
|
||||
<n-form-item label="显示首页 Banner">
|
||||
<div class="aksrt-inline-setting">
|
||||
@@ -111,13 +116,19 @@
|
||||
|
||||
<script setup>
|
||||
import { h, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { NButton, NImage, NTag, useDialog, useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
import { openMediaPicker } from '@/utils/media'
|
||||
import PageHeader from '@/components/PageHeader.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
const dialog = useDialog()
|
||||
|
||||
function goHeaderComponents() {
|
||||
router.push('/page-display/header-components')
|
||||
}
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
const settingsLoading = ref(false)
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container content-page">
|
||||
<PageHeader title="Hero 轮播管理" desc="管理首页 Hero 轮播数据,供固定首页和相关展示区域调用;标题、副标题可留空。" />
|
||||
<PageHeader title="Hero 轮播管理" desc="管理首页 Hero 幻灯片内容(图片/视频、文案、图层、跳转)。标题、副标题可留空。" />
|
||||
|
||||
<n-card class="aksrt-section-card" style="margin-bottom:16px;">
|
||||
<template #header><strong>Hero 显示开关</strong></template>
|
||||
<template #header>
|
||||
<div class="aksrt-card-header-row">
|
||||
<strong>Hero 显示开关</strong>
|
||||
<n-button size="small" secondary @click="goHeaderComponents">顶部多功能组件(搜索/卡片/轮播参数)</n-button>
|
||||
</div>
|
||||
</template>
|
||||
<n-form label-placement="left" label-width="160" class="aksrt-form">
|
||||
<n-form-item label="显示首页 Hero">
|
||||
<div class="aksrt-inline-setting">
|
||||
@@ -196,13 +201,19 @@
|
||||
|
||||
<script setup>
|
||||
import { h, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { NButton, NImage, NTag, useDialog, useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
import { openMediaPicker } from '@/utils/media'
|
||||
import PageHeader from '@/components/PageHeader.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
const dialog = useDialog()
|
||||
|
||||
function goHeaderComponents() {
|
||||
router.push('/page-display/header-components')
|
||||
}
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
const settingsLoading = ref(false)
|
||||
|
||||
@@ -184,9 +184,9 @@
|
||||
如需调整首页首屏运营区,请前往对应管理页配置幻灯片内容或顶部组件参数。
|
||||
</n-alert>
|
||||
<n-space>
|
||||
<n-button tag="a" href="#/page-display/hero" secondary type="primary">管理 Hero 轮播</n-button>
|
||||
<n-button tag="a" href="#/page-display/banner" secondary type="primary">管理 Banner 轮播</n-button>
|
||||
<n-button tag="a" href="#/page-display/header-components" secondary>
|
||||
<n-button secondary type="primary" @click="router.push('/page-display/hero')">管理 Hero 轮播</n-button>
|
||||
<n-button secondary type="primary" @click="router.push('/page-display/banner')">管理 Banner 轮播</n-button>
|
||||
<n-button secondary @click="router.push('/page-display/header-components')">
|
||||
顶部多功能组件
|
||||
</n-button>
|
||||
</n-space>
|
||||
@@ -204,10 +204,12 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
import PageHeader from '@/components/PageHeader.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
|
||||
@@ -178,6 +178,26 @@
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
#aksrt-admin-app .aksrt-card-header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#aksrt-admin-app .aksrt-card-header-row__left {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#aksrt-admin-app .aksrt-card-header-row__title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ==================== 卡片 header 行(标题 + 按钮) ==================== */
|
||||
#aksrt-admin-app .aksrt-card-header-row {
|
||||
display: flex;
|
||||
|
||||
@@ -46,7 +46,7 @@ export const MODULE_LIBRARY = [
|
||||
type: 'hero',
|
||||
label: 'Hero 首屏',
|
||||
icon: 'image',
|
||||
desc: '大图轮播首屏,支持叠加搜索框和图标卡片',
|
||||
desc: '固定首页骨架:大图轮播首屏。幻灯片内容在「页面&显示 → Hero 轮播」维护;搜索/卡片参数见「顶部多功能组件」。',
|
||||
defaultConfig: { with_search: true, show_cards: false, card_columns: 4, icon_cards: [] },
|
||||
fields: [
|
||||
{ key: 'with_search', type: 'switch', label: '显示搜索覆盖框' },
|
||||
@@ -58,7 +58,7 @@ export const MODULE_LIBRARY = [
|
||||
type: 'banner',
|
||||
label: 'Banner 轮播',
|
||||
icon: 'rectangle',
|
||||
desc: '正文区轮播条',
|
||||
desc: '固定首页骨架:正文区轮播条。幻灯片内容在「页面&显示 → Banner 轮播」维护。',
|
||||
defaultConfig: {},
|
||||
fields: [],
|
||||
},
|
||||
@@ -478,7 +478,7 @@ export const PRESET_TEMPLATES = [
|
||||
{
|
||||
key: 'resource_standard',
|
||||
label: '资源站标准',
|
||||
desc: 'Hero + Tabs + 最新资源 + 下载排行',
|
||||
desc: 'Hero(固定骨架)+ Tabs + 最新资源 + 下载排行',
|
||||
modules: [
|
||||
{ type: 'hero', config: {} },
|
||||
{ type: 'tabs', config: { count: 8, layout: 'grid-cms' } },
|
||||
@@ -489,7 +489,7 @@ export const PRESET_TEMPLATES = [
|
||||
{
|
||||
key: 'news',
|
||||
label: '资讯站',
|
||||
desc: 'Banner + 多个分类块 + 标签云',
|
||||
desc: 'Banner(固定骨架)+ 多个分类块 + 标签云',
|
||||
modules: [
|
||||
{ type: 'banner', config: {} },
|
||||
{ type: 'category', config: { count: 8, columns: 4, layout: 'card' } },
|
||||
@@ -500,7 +500,7 @@ export const PRESET_TEMPLATES = [
|
||||
{
|
||||
key: 'mixed',
|
||||
label: '混合站',
|
||||
desc: 'Hero + 最新文章 + 多个分类块',
|
||||
desc: 'Hero(固定骨架)+ 最新文章 + 多个分类块',
|
||||
modules: [
|
||||
{ type: 'hero', config: {} },
|
||||
{ type: 'recent', config: { post_type: 'post', count: 6, layout: 'card' } },
|
||||
|
||||
@@ -94,7 +94,8 @@ class AKSRT_Admin_Page_Display_Controller extends AKSRT_Admin_Controller_Base {
|
||||
'link_target' => get_post_meta($post->ID, '_link_target', true) ?: '_self',
|
||||
'text_align' => get_post_meta($post->ID, '_text_align', true) ?: 'left',
|
||||
'device_visibility' => get_post_meta($post->ID, '_device_visibility', true) ?: 'all',
|
||||
'enabled' => get_post_meta($post->ID, '_enabled', true) === '1',
|
||||
// Match frontend aksrt_get_banners(): missing _enabled means enabled.
|
||||
'enabled' => get_post_meta($post->ID, '_enabled', true) !== '0',
|
||||
'show_text' => get_post_meta($post->ID, '_show_text', true) !== '0',
|
||||
'show_meta' => get_post_meta($post->ID, '_show_meta', true) === '1',
|
||||
'kind' => get_post_meta($post->ID, '_aksrt_kind', true) ?: 'hero',
|
||||
@@ -102,7 +103,7 @@ class AKSRT_Admin_Page_Display_Controller extends AKSRT_Admin_Controller_Base {
|
||||
'text_size_pc' => get_post_meta($post->ID, '_text_size_pc', true) ?: '',
|
||||
'text_size_m' => get_post_meta($post->ID, '_text_size_m', true) ?: '',
|
||||
'text_parallax' => get_post_meta($post->ID, '_text_parallax', true) === '1',
|
||||
'layers' => is_string($layers_json) && $layers_json !== '' ? json_decode($layers_json, true) : [],
|
||||
'layers' => is_string($layers_json) && $layers_json !== '' ? (json_decode($layers_json, true) ?: []) : [],
|
||||
];
|
||||
}, $posts);
|
||||
return rest_ensure_response(['code' => 'success', 'data' => $banners]);
|
||||
@@ -113,6 +114,7 @@ class AKSRT_Admin_Page_Display_Controller extends AKSRT_Admin_Controller_Base {
|
||||
$kind = $this->normalize_banner_kind($params['kind'] ?? 'hero');
|
||||
$title = sanitize_text_field($params['title'] ?? '');
|
||||
$post_id = wp_insert_post([
|
||||
// Title is optional in admin UI; keep a non-empty WP title for list readability.
|
||||
'post_title' => '' !== $title ? $title : ('banner' === $kind ? 'Banner Slide' : 'Hero Slide'),
|
||||
'post_type' => 'aksrt_banner',
|
||||
'post_status' => 'publish',
|
||||
@@ -139,7 +141,7 @@ class AKSRT_Admin_Page_Display_Controller extends AKSRT_Admin_Controller_Base {
|
||||
update_post_meta($post_id, '_text_size_m', sanitize_text_field($params['text_size_m'] ?? ''));
|
||||
update_post_meta($post_id, '_text_parallax', !empty($params['text_parallax']) ? '1' : '0');
|
||||
if (isset($params['layers']) && is_array($params['layers'])) {
|
||||
update_post_meta($post_id, '_layers', wp_json_encode($params['layers']));
|
||||
update_post_meta($post_id, '_layers', wp_json_encode(array_values($params['layers'])));
|
||||
}
|
||||
|
||||
return rest_ensure_response(['code' => 'success', 'data' => ['id' => $post_id], 'message' => __('幻灯片已创建', 'aksrt')]);
|
||||
@@ -158,13 +160,8 @@ class AKSRT_Admin_Page_Display_Controller extends AKSRT_Admin_Controller_Base {
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$kind = $this->normalize_banner_kind($params['kind'] ?? get_post_meta($id, '_aksrt_kind', true) ?: 'hero');
|
||||
// Optional title: empty is allowed (UI shows “未命名”); do not 422.
|
||||
$title = sanitize_text_field($params['title'] ?? '');
|
||||
if ($title === '') {
|
||||
$existing = get_the_title($id);
|
||||
$title = is_string($existing) && $existing !== ''
|
||||
? $existing
|
||||
: ('banner' === $kind ? 'Banner Slide' : 'Hero Slide');
|
||||
}
|
||||
|
||||
wp_update_post([
|
||||
'ID' => $id,
|
||||
@@ -180,8 +177,8 @@ class AKSRT_Admin_Page_Display_Controller extends AKSRT_Admin_Controller_Base {
|
||||
update_post_meta($id, '_link_target', in_array($params['link_target'] ?? '', ['_blank', '_self'], true) ? $params['link_target'] : '_self');
|
||||
update_post_meta($id, '_text_align', in_array($params['text_align'] ?? '', ['left', 'center', 'right'], true) ? $params['text_align'] : 'left');
|
||||
update_post_meta($id, '_device_visibility', in_array($params['device_visibility'] ?? '', ['all', 'pc', 'mobile'], true) ? $params['device_visibility'] : 'all');
|
||||
update_post_meta($id, '_enabled', !empty($params['enabled']) ? '1' : '0');
|
||||
update_post_meta($id, '_show_text', !isset($params['show_text']) || !empty($params['show_text']) ? '1' : '0');
|
||||
update_post_meta($id, '_enabled', !array_key_exists('enabled', $params) || !empty($params['enabled']) ? '1' : '0');
|
||||
update_post_meta($id, '_show_text', !array_key_exists('show_text', $params) || !empty($params['show_text']) ? '1' : '0');
|
||||
update_post_meta($id, '_show_meta', !empty($params['show_meta']) ? '1' : '0');
|
||||
update_post_meta($id, '_text_size_pc', sanitize_text_field($params['text_size_pc'] ?? ''));
|
||||
update_post_meta($id, '_text_size_m', sanitize_text_field($params['text_size_m'] ?? ''));
|
||||
|
||||
Reference in New Issue
Block a user