fix(admin): replicate reference advanced-filter settings page 1:1
- Rewrite post_list/filter schema (PHP + JS) with the reference theme's
field set, order, defaults and dependencies; topics fields excluded
(no topics taxonomy), AKSRT private filter_* toggles dropped
- Support dynamic checkbox options in AksField (options as formData key)
so cat_custom_filter.custom_filters lists the custom_filter dimensions
- Map PostDisplay filter settings to the new keys without legacy fallback;
cat_custom_filter now carries lists/dropdown fieldsets and array filters
- Wire the archive chain to reference semantics: cat_custom_filter
overrides per-category filter projects, custom_filter_show gates the
default display, dimension chip groups render on category/tag archives
- Filter archive queries (main query + AJAX endpoint) by _aksrt_cf_{key}
meta with exact-quoted LIKE matching
- Add advanced-filter settings reference contract test
This commit is contained in:
@@ -481,6 +481,7 @@
|
|||||||
:key="field.key"
|
:key="field.key"
|
||||||
:schema="field"
|
:schema="field"
|
||||||
:model-value="fieldsetValue(field)"
|
:model-value="fieldsetValue(field)"
|
||||||
|
:form-data="formData"
|
||||||
@update:model-value="updateFieldsetValue(field.key, $event)"
|
@update:model-value="updateFieldsetValue(field.key, $event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -501,7 +502,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="item.fields && item.fields.length" class="aksrt-field__sortable-body">
|
<div v-if="item.fields && item.fields.length" class="aksrt-field__sortable-body">
|
||||||
<AksField v-for="field in item.fields" :key="field.key" :schema="field" :model-value="modelValue?.[item.key]?.[field.key] ?? field.default" @update:model-value="updateSortable(item.key, field.key, $event)" />
|
<AksField v-for="field in item.fields" :key="field.key" :schema="field" :model-value="modelValue?.[item.key]?.[field.key] ?? field.default" :form-data="formData" @update:model-value="updateSortable(item.key, field.key, $event)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -531,6 +532,7 @@
|
|||||||
v-if="matchDependency(field.dependsOn, item)"
|
v-if="matchDependency(field.dependsOn, item)"
|
||||||
:schema="field"
|
:schema="field"
|
||||||
:model-value="item[field.key]"
|
:model-value="item[field.key]"
|
||||||
|
:form-data="formData"
|
||||||
:disabled="schema.disabled"
|
:disabled="schema.disabled"
|
||||||
@update:model-value="updateRepeaterItem(index, field.key, $event)"
|
@update:model-value="updateRepeaterItem(index, field.key, $event)"
|
||||||
/>
|
/>
|
||||||
@@ -646,6 +648,7 @@
|
|||||||
:key="field.key"
|
:key="field.key"
|
||||||
:schema="field"
|
:schema="field"
|
||||||
:model-value="repeaterDraft[field.key]"
|
:model-value="repeaterDraft[field.key]"
|
||||||
|
:form-data="formData"
|
||||||
@update:model-value="updateRepeaterDraft(field.key, $event)"
|
@update:model-value="updateRepeaterDraft(field.key, $event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -696,6 +699,8 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
/** Current field value */
|
/** Current field value */
|
||||||
modelValue: { default: null },
|
modelValue: { default: null },
|
||||||
|
/** Whole-form data — used to resolve dynamic options (schema.options as string = formData key). */
|
||||||
|
formData: { type: Object, default: () => ({}) },
|
||||||
/** Error message to display */
|
/** Error message to display */
|
||||||
errorMessage: { type: String, default: '' },
|
errorMessage: { type: String, default: '' },
|
||||||
})
|
})
|
||||||
@@ -729,10 +734,22 @@ function updateFieldsetValue(key, value) {
|
|||||||
emit('update:modelValue', { ...current, [key]: value })
|
emit('update:modelValue', { ...current, [key]: value })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Normalize options: support both string[] and {label, value}[] */
|
/** Normalize options: support string[], {label, value}[], and dynamic formData-key references */
|
||||||
const normalizedOptions = computed(() => {
|
const normalizedOptions = computed(() => {
|
||||||
const opts = props.schema.options || []
|
const opts = props.schema.options
|
||||||
return opts.map(opt => {
|
if (typeof opts === 'string') {
|
||||||
|
// 动态选项:引用 formData 中同名字段(如 custom_filter 维度定义 [{key,name,vals}])。
|
||||||
|
const source = props.formData?.[opts]
|
||||||
|
if (!Array.isArray(source)) return []
|
||||||
|
return source
|
||||||
|
.map(item => {
|
||||||
|
const value = String(item?.key ?? '')
|
||||||
|
if (value === '') return null
|
||||||
|
return { label: String(item?.name ?? item?.key ?? value), value }
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
return (Array.isArray(opts) ? opts : []).map(opt => {
|
||||||
if (typeof opt === 'string') return { label: opt, value: opt }
|
if (typeof opt === 'string') return { label: opt, value: opt }
|
||||||
if (typeof opt === 'object' && opt !== null) {
|
if (typeof opt === 'object' && opt !== null) {
|
||||||
return { ...opt, label: opt.label ?? opt.name ?? opt.value, value: opt.value ?? opt.label ?? opt.name }
|
return { ...opt, label: opt.label ?? opt.name ?? opt.value, value: opt.value ?? opt.label ?? opt.name }
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
v-if="isFieldVisible(sub)"
|
v-if="isFieldVisible(sub)"
|
||||||
:schema="sub"
|
:schema="sub"
|
||||||
:model-value="getFieldValue(sub.key)"
|
:model-value="getFieldValue(sub.key)"
|
||||||
|
:form-data="formData"
|
||||||
:error-message="errors?.[sub.key]"
|
:error-message="errors?.[sub.key]"
|
||||||
@update:model-value="setFieldValue(sub.key, $event)"
|
@update:model-value="setFieldValue(sub.key, $event)"
|
||||||
@action="emit('field-action', $event)"
|
@action="emit('field-action', $event)"
|
||||||
@@ -89,6 +90,7 @@
|
|||||||
<AksField
|
<AksField
|
||||||
:schema="field"
|
:schema="field"
|
||||||
:model-value="getFieldValue(field.key)"
|
:model-value="getFieldValue(field.key)"
|
||||||
|
:form-data="formData"
|
||||||
:error-message="errors?.[field.key]"
|
:error-message="errors?.[field.key]"
|
||||||
@update:model-value="setFieldValue(field.key, $event)"
|
@update:model-value="setFieldValue(field.key, $event)"
|
||||||
@action="emit('field-action', $event)"
|
@action="emit('field-action', $event)"
|
||||||
@@ -98,6 +100,7 @@
|
|||||||
v-else
|
v-else
|
||||||
:schema="field"
|
:schema="field"
|
||||||
:model-value="getFieldValue(field.key)"
|
:model-value="getFieldValue(field.key)"
|
||||||
|
:form-data="formData"
|
||||||
:error-message="errors?.[field.key]"
|
:error-message="errors?.[field.key]"
|
||||||
@update:model-value="setFieldValue(field.key, $event)"
|
@update:model-value="setFieldValue(field.key, $event)"
|
||||||
@action="emit('field-action', $event)"
|
@action="emit('field-action', $event)"
|
||||||
|
|||||||
@@ -1,101 +1,128 @@
|
|||||||
export const filterSchema = {
|
export const filterSchema = {
|
||||||
key: 'post_list.filter',
|
key: 'post_list.filter',
|
||||||
title: '高级筛选',
|
title: '高级筛选',
|
||||||
icon: 'fa fas fa-filter',
|
icon: 'fa fas fa-align-left',
|
||||||
description: '为文章列表配置筛选栏、排序方式和分类筛选功能。',
|
description: '通过自定义筛选类型实现更精细化的内容筛选,可按分类绑定不同的筛选项目。',
|
||||||
defaults: {
|
defaults: {
|
||||||
filter_enabled: false,
|
custom_filter_show: [],
|
||||||
filter_categories: true,
|
|
||||||
filter_tags: false,
|
|
||||||
filter_sort: true,
|
|
||||||
filter_ajax_enabled: true,
|
|
||||||
filter_cat_show_cat: true,
|
|
||||||
filter_cat_show_tag: false,
|
|
||||||
filter_tag_show_cat: true,
|
|
||||||
filter_tag_show_tag: false,
|
|
||||||
custom_filter_show: ['cat', 'tag'],
|
|
||||||
custom_filter: [],
|
custom_filter: [],
|
||||||
cat_custom_filter: [],
|
cat_custom_filter: [],
|
||||||
},
|
},
|
||||||
groups: [
|
groups: [
|
||||||
{
|
{
|
||||||
key: 'basic',
|
key: 'advanced_filter',
|
||||||
title: '筛选基础',
|
title: '高级筛选',
|
||||||
icon: 'fa fas fa-filter',
|
icon: 'fa fas fa-align-left',
|
||||||
fields: [
|
|
||||||
{ key: 'filter_enabled', label: '启用筛选栏', type: 'switch', default: false },
|
|
||||||
{ key: 'custom_filter_show', label: '筛选显示页面', type: 'checkbox', default: ['cat', 'tag'], options: [{ label: '分类页', value: 'cat' }, { label: '标签页', value: 'tag' }], help: '选择在哪些页面显示筛选栏', dependsOn: { key: 'filter_enabled', value: true } },
|
|
||||||
{ key: 'filter_categories', label: '分类筛选', type: 'switch', default: true, dependsOn: { key: 'filter_enabled', value: true } },
|
|
||||||
{ key: 'filter_tags', label: '标签筛选', type: 'switch', default: false, dependsOn: { key: 'filter_enabled', value: true } },
|
|
||||||
{ key: 'filter_sort', label: '排序选项', type: 'switch', default: true, dependsOn: { key: 'filter_enabled', value: true } },
|
|
||||||
{ key: 'filter_ajax_enabled', label: 'AJAX 无刷新筛选', type: 'switch', default: true, help: '启用后分类/标签/排序切换将通过 AJAX 加载,无需刷新页面', dependsOn: { key: 'filter_enabled', value: true } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'ajax',
|
|
||||||
title: 'AJAX 筛选维度',
|
|
||||||
icon: 'fa fas fa-clone',
|
|
||||||
fields: [
|
|
||||||
{ key: 'filter_cat_show_cat', label: '分类页:显示分类筛选', type: 'switch', default: true },
|
|
||||||
{ key: 'filter_cat_show_tag', label: '分类页:显示标签筛选', type: 'switch', default: false },
|
|
||||||
{ key: 'filter_tag_show_cat', label: '标签页:显示分类筛选', type: 'switch', default: true },
|
|
||||||
{ key: 'filter_tag_show_tag', label: '标签页:显示标签筛选', type: 'switch', default: false },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'custom_filter',
|
|
||||||
title: '自定义筛选维度',
|
|
||||||
icon: 'fa fas fa-sliders-h',
|
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'filter_intro',
|
||||||
|
type: 'content',
|
||||||
|
ui_only: true,
|
||||||
|
content: '<p style="margin:0;font-size:12px;color:var(--aksrt-color-muted);line-height:1.7;"><b>文章高级筛选功能:</b>通过此功能可实现更加复杂、精细化的内容筛选<br/>添加筛选类型和内容值之后,编写文章时候,可以进行选择,且在前台也可以实现精准筛选<br/>例如影视网站:[类型]有[剧情/喜剧/悬疑/惊悚/犯罪],[地区]有[大陆/美国/日韩/港台/印度],[年份]有[2022/2021/2020/10年代/00年代]等<br/><span style="color:#d48806;">注意事项:添加类型key时候,只能使用英文加下划线,不能有空格,且尽量复杂一点(不能为tag,category等系统已存在的类型),避免与其他mate_key重复!类型名称不能为分类、标签等系统已存在的类型</span><br/><a href="https://www.zibll.com/15886.html" target="_blank">查看官网教程</a></p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'custom_filter_show',
|
||||||
|
label: '高级筛选默认显示',
|
||||||
|
type: 'checkbox',
|
||||||
|
default: [],
|
||||||
|
options: [
|
||||||
|
{ label: '分类页面显示', value: 'cat' },
|
||||||
|
{ label: '标签页面显示', value: 'tag' },
|
||||||
|
],
|
||||||
|
description: '勾选后将会在对应页面默认显示全部筛选,您可以在下方为单独的分类添加自定义筛选',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'custom_filter',
|
key: 'custom_filter',
|
||||||
label: '筛选维度定义',
|
label: '自定义高级筛选明细',
|
||||||
type: 'repeater',
|
type: 'repeater',
|
||||||
default: [],
|
default: [],
|
||||||
addText: '添加筛选维度',
|
addText: '添加筛选类别',
|
||||||
help: '每项定义一个筛选维度(如"类型""地区""年份"),及其可选值列表',
|
description: '添加筛选类型及明细',
|
||||||
fields: [
|
fields: [
|
||||||
{ key: 'name', label: '维度名称', type: 'input', default: '', placeholder: '如:类型' },
|
{ key: 'name', label: '类型名称', type: 'input', default: '' },
|
||||||
{ key: 'key', label: '维度标识', type: 'input', default: '', placeholder: '如:type(英文下划线)' },
|
{ key: 'key', label: '类型key', type: 'input', default: '', description: '必填,只能使用英文加下划线,不能有空格!尽量复杂一点,且配置唯一前缀,以避免与其他mate_key重复,例如(axb_type,axb_year)' },
|
||||||
{
|
{
|
||||||
key: 'vals',
|
key: 'vals',
|
||||||
label: '可选值',
|
label: '可选择的值',
|
||||||
type: 'repeater',
|
type: 'repeater',
|
||||||
default: [],
|
default: [],
|
||||||
addText: '添加可选值',
|
addText: '添加值',
|
||||||
fields: [
|
fields: [
|
||||||
{ key: 'key', label: '标识', type: 'input', default: '', placeholder: '如:action' },
|
{ key: 'key', label: '值', type: 'input', default: '' },
|
||||||
{ key: 'name', label: '显示名', type: 'input', default: '', placeholder: '如:动作' },
|
{ key: 'name', label: '显示名称', type: 'input', default: '' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'cat_custom_filter',
|
|
||||||
title: '按分类绑定筛选',
|
|
||||||
icon: 'fa fas fa-object-group',
|
|
||||||
fields: [
|
|
||||||
{
|
{
|
||||||
key: 'cat_custom_filter',
|
key: 'cat_custom_filter',
|
||||||
label: '分类筛选绑定',
|
label: '自定义分类的高级筛选',
|
||||||
type: 'repeater',
|
type: 'repeater',
|
||||||
default: [],
|
default: [],
|
||||||
addText: '添加绑定规则',
|
addText: '添加自定义分类筛选',
|
||||||
help: '为指定分类绑定不同的筛选维度组合,实现分类级别的差异化筛选',
|
description: '在此处为不同的分类添加不同的筛选项目,实现更加精细化的筛选。同时你也可以在页面显示/分类页面设置分类的默认筛选方式',
|
||||||
fields: [
|
fields: [
|
||||||
{ key: 'cats', label: '绑定分类', type: 'taxonomy_select', taxonomy: 'category', multiple: true, default: [] },
|
{ key: 'cats', label: '绑定分类', type: 'taxonomy_select', taxonomy: 'category', multiple: true, default: [], description: '支持添加多个分类' },
|
||||||
{ key: 'cat_s', label: '显示分类筛选', type: 'switch', default: false },
|
{ key: 'cat_s', label: '分类筛选', type: 'switch', default: false, description: '在所选分类页显示的分类筛选列表' },
|
||||||
{ key: 'tag_s', label: '显示标签筛选', type: 'switch', default: false },
|
{
|
||||||
{ key: 'orderby_s', label: '显示排序按钮', type: 'switch', default: true },
|
key: 'cat_lists',
|
||||||
|
label: ' ',
|
||||||
|
type: 'fieldset',
|
||||||
|
default: { lists: [], dropdown: false },
|
||||||
|
dependsOn: { key: 'cat_s', value: true },
|
||||||
|
fields: [
|
||||||
|
{ key: 'lists', label: '按钮列表', type: 'async_select', source: 'categories', multiple: true, sortable: true, default: [], description: '选择并排序需要显示的按钮,建议选择同级内容,会自动获取二级三级内容。所选按钮内没有文章则不会显示' },
|
||||||
|
{ key: 'dropdown', label: '下拉列表', type: 'switch', default: false, description: '用下拉框显示更多内容' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ key: 'tag_s', label: '标签筛选', type: 'switch', default: false, description: '在所选分类页显示的标签筛选列表' },
|
||||||
|
{
|
||||||
|
key: 'tag_lists',
|
||||||
|
label: ' ',
|
||||||
|
type: 'fieldset',
|
||||||
|
default: { lists: [], dropdown: false },
|
||||||
|
dependsOn: { key: 'tag_s', value: true },
|
||||||
|
fields: [
|
||||||
|
{ key: 'lists', label: '按钮列表', type: 'async_select', source: 'tags', multiple: true, sortable: true, default: [], description: '选择并排序需要显示的按钮' },
|
||||||
|
{ key: 'dropdown', label: '下拉列表', type: 'switch', default: false, description: '用下拉框显示更多内容' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ key: 'orderby_s', label: '排序方式', type: 'switch', default: true, description: '在所选分类页显示排序方式按钮' },
|
||||||
|
{
|
||||||
|
key: 'orderby_lists',
|
||||||
|
label: ' ',
|
||||||
|
type: 'fieldset',
|
||||||
|
default: { lists: ['modified', 'views', 'like', 'comment_count'], dropdown: false },
|
||||||
|
dependsOn: { key: 'orderby_s', value: true },
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'lists',
|
||||||
|
label: '显示排序方式',
|
||||||
|
type: 'select',
|
||||||
|
multiple: true,
|
||||||
|
sortable: true,
|
||||||
|
default: ['modified', 'views', 'like', 'comment_count'],
|
||||||
|
options: [
|
||||||
|
{ label: '更新', value: 'modified' },
|
||||||
|
{ label: '发布', value: 'date' },
|
||||||
|
{ label: '浏览', value: 'views' },
|
||||||
|
{ label: '点赞', value: 'like' },
|
||||||
|
{ label: '评论', value: 'comment_count' },
|
||||||
|
{ label: '收藏', value: 'favorite' },
|
||||||
|
{ label: '随机', value: 'rand' },
|
||||||
|
],
|
||||||
|
description: '选择需要的排序方式按钮',
|
||||||
|
},
|
||||||
|
{ key: 'dropdown', label: '更多排序方式', type: 'switch', default: false, description: '用下拉框显示全部排序方式' },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'custom_filters',
|
key: 'custom_filters',
|
||||||
label: '启用的自定义维度',
|
label: '自定义高级筛选',
|
||||||
type: 'input',
|
type: 'checkbox',
|
||||||
default: '',
|
default: [],
|
||||||
placeholder: '逗号分隔维度标识,如:type,region,year',
|
options: 'custom_filter',
|
||||||
help: '留空表示使用全部已定义的筛选维度',
|
description: '在所选分类页显示的自定义高级筛选项目。如果此处没有选项,或者您修改了上方的文章高级筛选明细,请先保存主题配置并刷新页面后再设置此处',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
var state = {
|
var state = {
|
||||||
filter_cat: "",
|
filter_cat: "",
|
||||||
filter_tag: "",
|
filter_tag: "",
|
||||||
|
custom: {},
|
||||||
sort: "date",
|
sort: "date",
|
||||||
page: 1
|
page: 1
|
||||||
};
|
};
|
||||||
@@ -32,6 +33,14 @@
|
|||||||
if (activeTag) state.filter_tag = activeTag.dataset.filterTag || "";
|
if (activeTag) state.filter_tag = activeTag.dataset.filterTag || "";
|
||||||
var activeSort = bar.querySelector("[data-filter-sort].is-active");
|
var activeSort = bar.querySelector("[data-filter-sort].is-active");
|
||||||
if (activeSort) state.sort = activeSort.dataset.filterSort || "date";
|
if (activeSort) state.sort = activeSort.dataset.filterSort || "date";
|
||||||
|
// 自定义高级筛选维度:以当前 URL 参数为准(SSR active 状态与 URL 一致)。
|
||||||
|
if (bar.querySelector("[data-filter-dim]")) {
|
||||||
|
new URLSearchParams(window.location.search).forEach(function (value, key) {
|
||||||
|
if (bar.querySelector('[data-filter-dim="' + key + '"]')) {
|
||||||
|
state.custom[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function buildUrl() {
|
function buildUrl() {
|
||||||
var params = new URLSearchParams();
|
var params = new URLSearchParams();
|
||||||
@@ -39,6 +48,7 @@
|
|||||||
params.set("term_id", String(termId));
|
params.set("term_id", String(termId));
|
||||||
if (state.filter_cat) params.set("filter_cat", state.filter_cat);
|
if (state.filter_cat) params.set("filter_cat", state.filter_cat);
|
||||||
if (state.filter_tag) params.set("filter_tag", state.filter_tag);
|
if (state.filter_tag) params.set("filter_tag", state.filter_tag);
|
||||||
|
if (Object.keys(state.custom).length) params.set("custom_filter", JSON.stringify(state.custom));
|
||||||
|
|
||||||
params.set("sort", state.sort || "date");
|
params.set("sort", state.sort || "date");
|
||||||
params.set("page", String(state.page));
|
params.set("page", String(state.page));
|
||||||
@@ -55,6 +65,10 @@
|
|||||||
bar.querySelectorAll("[data-filter-sort]").forEach(function (el) {
|
bar.querySelectorAll("[data-filter-sort]").forEach(function (el) {
|
||||||
el.classList.toggle("is-active", (el.dataset.filterSort || "date") === state.sort);
|
el.classList.toggle("is-active", (el.dataset.filterSort || "date") === state.sort);
|
||||||
});
|
});
|
||||||
|
bar.querySelectorAll("[data-filter-dim]").forEach(function (el) {
|
||||||
|
var current = state.custom[el.dataset.filterDim] || "";
|
||||||
|
el.classList.toggle("is-active", (el.dataset.filterDimValue || "") !== "" && (el.dataset.filterDimValue || "") === current);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showLoading() {
|
function showLoading() {
|
||||||
@@ -110,8 +124,14 @@
|
|||||||
url.searchParams.delete("paged");
|
url.searchParams.delete("paged");
|
||||||
url.searchParams.delete("aksrt_filter_cat");
|
url.searchParams.delete("aksrt_filter_cat");
|
||||||
url.searchParams.delete("aksrt_filter_tag");
|
url.searchParams.delete("aksrt_filter_tag");
|
||||||
|
bar.querySelectorAll("[data-filter-dim]").forEach(function (el) {
|
||||||
|
url.searchParams.delete(el.dataset.filterDim);
|
||||||
|
});
|
||||||
if (state.filter_cat) url.searchParams.set("aksrt_filter_cat", state.filter_cat);
|
if (state.filter_cat) url.searchParams.set("aksrt_filter_cat", state.filter_cat);
|
||||||
if (state.filter_tag) url.searchParams.set("aksrt_filter_tag", state.filter_tag);
|
if (state.filter_tag) url.searchParams.set("aksrt_filter_tag", state.filter_tag);
|
||||||
|
Object.keys(state.custom).forEach(function (key) {
|
||||||
|
url.searchParams.set(key, state.custom[key]);
|
||||||
|
});
|
||||||
if (state.sort) url.searchParams.set("orderby", state.sort);
|
if (state.sort) url.searchParams.set("orderby", state.sort);
|
||||||
if (state.page > 1) url.searchParams.set("paged", String(state.page));
|
if (state.page > 1) url.searchParams.set("paged", String(state.page));
|
||||||
history.pushState({ aksrtArchive: true, state: Object.assign({}, state) }, "", url.toString());
|
history.pushState({ aksrtArchive: true, state: Object.assign({}, state) }, "", url.toString());
|
||||||
@@ -123,7 +143,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
bar.addEventListener("click", function (e) {
|
bar.addEventListener("click", function (e) {
|
||||||
var chip = e.target.closest("[data-filter-cat], [data-filter-tag], [data-filter-sort]");
|
var chip = e.target.closest("[data-filter-cat], [data-filter-tag], [data-filter-sort], [data-filter-dim]");
|
||||||
if (!chip) return;
|
if (!chip) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -149,6 +169,18 @@
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (chip.dataset.filterDim !== undefined) {
|
||||||
|
var dimKey = chip.dataset.filterDim;
|
||||||
|
var dimValue = chip.dataset.filterDimValue || "";
|
||||||
|
if (dimValue !== (state.custom[dimKey] || "")) {
|
||||||
|
if (dimValue === "") {
|
||||||
|
delete state.custom[dimKey];
|
||||||
|
} else {
|
||||||
|
state.custom[dimKey] = dimValue;
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
state.page = 1;
|
state.page = 1;
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -73,6 +73,16 @@ if (!empty($archive_settings['ajax_tag_enabled']) && function_exists('aksrt_arch
|
|||||||
$filter_groups[] = $group;
|
$filter_groups[] = $group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!empty($archive_settings['custom_filter_enabled']) && function_exists('aksrt_archive_build_custom_filter_groups')) {
|
||||||
|
$filter_groups = array_merge(
|
||||||
|
$filter_groups,
|
||||||
|
aksrt_archive_build_custom_filter_groups(
|
||||||
|
(array) ($archive_settings['custom_dimensions'] ?? []),
|
||||||
|
$archive_settings['custom_filter_keys'] ?? 'all',
|
||||||
|
$ajax_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (function_exists('aksrt_archive_open_shell')) {
|
if (function_exists('aksrt_archive_open_shell')) {
|
||||||
aksrt_archive_open_shell([
|
aksrt_archive_open_shell([
|
||||||
|
|||||||
@@ -262,3 +262,71 @@ if (!function_exists('aksrt_archive_render_filter_bar')) :
|
|||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
endif;
|
endif;
|
||||||
|
|
||||||
|
if (!function_exists('aksrt_archive_build_custom_filter_groups')) :
|
||||||
|
/**
|
||||||
|
* Build custom advanced-filter dimension groups.
|
||||||
|
*
|
||||||
|
* Reference: zib_get_option_custom_filter_but — one labeled chip group per
|
||||||
|
* dimension; when a value is active an extra "all" chip prepends to clear it.
|
||||||
|
*
|
||||||
|
* @param array<int,array<string,mixed>> $dimensions Dimension definitions (custom_filter settings).
|
||||||
|
* @param mixed $keys 'all' or list of enabled dimension keys.
|
||||||
|
* @param bool $ajax Attach AJAX attributes.
|
||||||
|
* @return array<int,array<string,mixed>>
|
||||||
|
*/
|
||||||
|
function aksrt_archive_build_custom_filter_groups(array $dimensions, $keys, bool $ajax): array {
|
||||||
|
$groups = [];
|
||||||
|
foreach ($dimensions as $dimension) {
|
||||||
|
$dim_key = sanitize_key((string) ($dimension['key'] ?? ''));
|
||||||
|
$dim_name = sanitize_text_field((string) ($dimension['name'] ?? $dim_key));
|
||||||
|
$vals = is_array($dimension['vals'] ?? null) ? $dimension['vals'] : [];
|
||||||
|
if ($dim_key === '' || $vals === []) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($keys !== 'all' && !in_array($dim_key, (array) $keys, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = isset($_GET[$dim_key]) ? sanitize_key(wp_unslash((string) $_GET[$dim_key])) : '';
|
||||||
|
$chips = [];
|
||||||
|
$has_focus = false;
|
||||||
|
foreach ($vals as $val) {
|
||||||
|
$value_key = sanitize_key((string) ($val['key'] ?? ''));
|
||||||
|
$value_name = sanitize_text_field((string) ($val['name'] ?? $value_key));
|
||||||
|
if ($value_key === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$is_active = $current === $value_key;
|
||||||
|
$chips[] = [
|
||||||
|
'label' => $value_name,
|
||||||
|
'url' => $is_active
|
||||||
|
? remove_query_arg([$dim_key, 'paged'])
|
||||||
|
: add_query_arg([$dim_key => $value_key], remove_query_arg('paged')),
|
||||||
|
'active' => $is_active,
|
||||||
|
'attrs' => $ajax ? ' data-filter-dim="' . esc_attr($dim_key) . '" data-filter-dim-value="' . esc_attr($value_key) . '"' : '',
|
||||||
|
];
|
||||||
|
if ($is_active) {
|
||||||
|
$has_focus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($chips === []) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($has_focus) {
|
||||||
|
array_unshift($chips, [
|
||||||
|
'label' => __('全部', 'aksrt'),
|
||||||
|
'url' => remove_query_arg([$dim_key, 'paged']),
|
||||||
|
'active' => false,
|
||||||
|
'attrs' => $ajax ? ' data-filter-dim="' . esc_attr($dim_key) . '" data-filter-dim-value=""' : '',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$groups[] = [
|
||||||
|
'label' => $dim_name,
|
||||||
|
'chips' => $chips,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $groups;
|
||||||
|
}
|
||||||
|
endif;
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ class GlobalController extends ControllerBase {
|
|||||||
'term_id' => ['type' => 'integer', 'default' => 0, 'sanitize_callback' => 'absint'],
|
'term_id' => ['type' => 'integer', 'default' => 0, 'sanitize_callback' => 'absint'],
|
||||||
'filter_cat' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
'filter_cat' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||||
'filter_tag' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
'filter_tag' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||||
|
'custom_filter' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||||
'sort' => ['type' => 'string', 'default' => 'date', 'sanitize_callback' => 'sanitize_key'],
|
'sort' => ['type' => 'string', 'default' => 'date', 'sanitize_callback' => 'sanitize_key'],
|
||||||
'page' => ['type' => 'integer', 'default' => 1, 'sanitize_callback' => 'absint'],
|
'page' => ['type' => 'integer', 'default' => 1, 'sanitize_callback' => 'absint'],
|
||||||
],
|
],
|
||||||
@@ -209,6 +210,44 @@ class GlobalController extends ControllerBase {
|
|||||||
if ($tax_query !== []) {
|
if ($tax_query !== []) {
|
||||||
$args['tax_query'] = $tax_query;
|
$args['tax_query'] = $tax_query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 自定义高级筛选维度(custom_filter=JSON {dim_key:value};参考主题 meta_query LIKE 语义)。
|
||||||
|
$custom_raw = (string) $request->get_param('custom_filter');
|
||||||
|
$custom = $custom_raw !== '' ? json_decode($custom_raw, true) : [];
|
||||||
|
if (is_array($custom) && $custom !== []) {
|
||||||
|
$filter_cfg = \AKSRT\Settings\Settings::get_post_filter_settings();
|
||||||
|
$allowed = [];
|
||||||
|
foreach ($filter_cfg['custom_filter'] as $dimension) {
|
||||||
|
$dim_key = sanitize_key((string) ($dimension['key'] ?? ''));
|
||||||
|
if ($dim_key === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$vals = [];
|
||||||
|
foreach ((array) ($dimension['vals'] ?? []) as $val) {
|
||||||
|
$vkey = sanitize_key((string) ($val['key'] ?? ''));
|
||||||
|
if ($vkey !== '') {
|
||||||
|
$vals[] = $vkey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$allowed[$dim_key] = $vals;
|
||||||
|
}
|
||||||
|
$meta_query = [];
|
||||||
|
foreach ($custom as $dim_key => $dim_value) {
|
||||||
|
$dim_key = sanitize_key((string) $dim_key);
|
||||||
|
$dim_value = sanitize_key((string) $dim_value);
|
||||||
|
if ($dim_key === '' || $dim_value === '' || !isset($allowed[$dim_key]) || !in_array($dim_value, $allowed[$dim_key], true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$meta_query[] = [
|
||||||
|
'key' => '_aksrt_cf_' . $dim_key,
|
||||||
|
'value' => '"' . $dim_value . '"',
|
||||||
|
'compare' => 'LIKE',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if ($meta_query !== []) {
|
||||||
|
$args['meta_query'] = $meta_query;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (function_exists('aksrt_homepage_apply_named_sort_args')) {
|
if (function_exists('aksrt_homepage_apply_named_sort_args')) {
|
||||||
$args = aksrt_homepage_apply_named_sort_args($args, $sort);
|
$args = aksrt_homepage_apply_named_sort_args($args, $sort);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,43 +129,49 @@ class SchemaPostList {
|
|||||||
'filter' => [
|
'filter' => [
|
||||||
'key' => 'filter',
|
'key' => 'filter',
|
||||||
'title' => '高级筛选',
|
'title' => '高级筛选',
|
||||||
'version' => '1.0.0',
|
'version' => '2.0.0',
|
||||||
'option_key' => 'aksrt_settings',
|
'option_key' => 'aksrt_settings',
|
||||||
'groups' => [
|
'groups' => [
|
||||||
['key' => 'basic', 'title' => '筛选基础', 'fields' => [
|
['key' => 'advanced_filter', 'title' => '高级筛选', 'fields' => [
|
||||||
['key' => 'filter_enabled', 'label' => '启用筛选栏', 'type' => 'switch', 'default' => false],
|
['key' => 'custom_filter_show', 'label' => '高级筛选默认显示', 'type' => 'checkbox', 'default' => [], 'options' => [
|
||||||
['key' => 'custom_filter_show', 'label' => '筛选显示页面', 'type' => 'checkbox', 'default' => ['cat', 'tag'], 'options' => [
|
['label' => '分类页面显示', 'value' => 'cat'],
|
||||||
['label' => '分类页', 'value' => 'cat'],
|
['label' => '标签页面显示', 'value' => 'tag'],
|
||||||
['label' => '标签页', 'value' => 'tag'],
|
], 'description' => '勾选后将会在对应页面默认显示全部筛选,您可以在下方为单独的分类添加自定义筛选'],
|
||||||
], 'help' => '选择在哪些页面显示筛选栏'],
|
['key' => 'custom_filter', 'label' => '自定义高级筛选明细', 'type' => 'repeater', 'default' => [], 'addText' => '添加筛选类别', 'description' => '添加筛选类型及明细', 'fields' => [
|
||||||
['key' => 'filter_categories', 'label' => '分类筛选', 'type' => 'switch', 'default' => true],
|
['key' => 'name', 'label' => '类型名称', 'type' => 'input', 'default' => ''],
|
||||||
['key' => 'filter_tags', 'label' => '标签筛选', 'type' => 'switch', 'default' => false],
|
['key' => 'key', 'label' => '类型key', 'type' => 'input', 'default' => '', 'description' => '必填,只能使用英文加下划线,不能有空格!尽量复杂一点,且配置唯一前缀,以避免与其他mate_key重复,例如(axb_type,axb_year)'],
|
||||||
['key' => 'filter_sort', 'label' => '排序选项', 'type' => 'switch', 'default' => true],
|
['key' => 'vals', 'label' => '可选择的值', 'type' => 'repeater', 'default' => [], 'addText' => '添加值', 'fields' => [
|
||||||
['key' => 'filter_ajax_enabled', 'label' => 'AJAX 无刷新筛选', 'type' => 'switch', 'default' => true, 'help' => '启用后分类/标签/排序切换将通过 AJAX 加载,无需刷新页面'],
|
['key' => 'key', 'label' => '值', 'type' => 'input', 'default' => ''],
|
||||||
]],
|
['key' => 'name', 'label' => '显示名称', 'type' => 'input', 'default' => ''],
|
||||||
['key' => 'ajax_dimensions', 'title' => 'AJAX 筛选维度', 'fields' => [
|
|
||||||
['key' => 'filter_cat_show_cat', 'label' => '分类页:显示分类筛选', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'filter_enabled', 'value' => true]],
|
|
||||||
['key' => 'filter_cat_show_tag', 'label' => '分类页:显示标签筛选', 'type' => 'switch', 'default' => false, 'dependsOn' => ['key' => 'filter_enabled', 'value' => true]],
|
|
||||||
['key' => 'filter_tag_show_cat', 'label' => '标签页:显示分类筛选', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'filter_enabled', 'value' => true]],
|
|
||||||
['key' => 'filter_tag_show_tag', 'label' => '标签页:显示标签筛选', 'type' => 'switch', 'default' => false, 'dependsOn' => ['key' => 'filter_enabled', 'value' => true]],
|
|
||||||
]],
|
|
||||||
['key' => 'custom_filter', 'title' => '自定义筛选维度', 'fields' => [
|
|
||||||
['key' => 'custom_filter', 'label' => '筛选维度定义', 'type' => 'repeater', 'default' => [], 'dependsOn' => ['key' => 'filter_enabled', 'value' => true], 'help' => '每项定义一个筛选维度(如"类型""地区""年份"),及其可选值列表', 'fields' => [
|
|
||||||
['key' => 'name', 'label' => '维度名称', 'type' => 'input', 'default' => ''],
|
|
||||||
['key' => 'key', 'label' => '维度标识', 'type' => 'input', 'default' => ''],
|
|
||||||
['key' => 'vals', 'label' => '可选值', 'type' => 'repeater', 'default' => [], 'fields' => [
|
|
||||||
['key' => 'key', 'label' => '标识', 'type' => 'input', 'default' => ''],
|
|
||||||
['key' => 'name', 'label' => '显示名', 'type' => 'input', 'default' => ''],
|
|
||||||
]],
|
]],
|
||||||
]],
|
]],
|
||||||
]],
|
['key' => 'cat_custom_filter', 'label' => '自定义分类的高级筛选', 'type' => 'repeater', 'default' => [], 'addText' => '添加自定义分类筛选', 'description' => '在此处为不同的分类添加不同的筛选项目,实现更加精细化的筛选。同时你也可以在页面显示/分类页面设置分类的默认筛选方式', 'fields' => [
|
||||||
['key' => 'cat_custom_filter', 'title' => '按分类绑定筛选', 'fields' => [
|
['key' => 'cats', 'label' => '绑定分类', 'type' => 'taxonomy_select', 'taxonomy' => 'category', 'multiple' => true, 'default' => [], 'description' => '支持添加多个分类'],
|
||||||
['key' => 'cat_custom_filter', 'label' => '分类筛选绑定', 'type' => 'repeater', 'default' => [], 'dependsOn' => ['key' => 'filter_enabled', 'value' => true], 'help' => '为指定分类绑定不同的筛选维度组合', 'fields' => [
|
['key' => 'cat_s', 'label' => '分类筛选', 'type' => 'switch', 'default' => false, 'description' => '在所选分类页显示的分类筛选列表'],
|
||||||
['key' => 'cats', 'label' => '绑定分类', 'type' => 'taxonomy_select', 'taxonomy' => 'category', 'multiple' => true, 'default' => []],
|
['key' => 'cat_lists', 'label' => ' ', 'type' => 'fieldset', 'default' => ['lists' => [], 'dropdown' => false], 'dependsOn' => ['key' => 'cat_s', 'value' => true], 'fields' => [
|
||||||
['key' => 'cat_s', 'label' => '显示分类筛选', 'type' => 'switch', 'default' => false],
|
['key' => 'lists', 'label' => '按钮列表', 'type' => 'async_select', 'source' => 'categories', 'multiple' => true, 'sortable' => true, 'default' => [], 'description' => '选择并排序需要显示的按钮,建议选择同级内容,会自动获取二级三级内容。所选按钮内没有文章则不会显示'],
|
||||||
['key' => 'tag_s', 'label' => '显示标签筛选', 'type' => 'switch', 'default' => false],
|
['key' => 'dropdown', 'label' => '下拉列表', 'type' => 'switch', 'default' => false, 'description' => '用下拉框显示更多内容'],
|
||||||
['key' => 'orderby_s', 'label' => '显示排序按钮', 'type' => 'switch', 'default' => true],
|
]],
|
||||||
['key' => 'custom_filters', 'label' => '启用的自定义维度', 'type' => 'input', 'default' => '', 'help' => '逗号分隔维度标识,留空使用全部'],
|
['key' => 'tag_s', 'label' => '标签筛选', 'type' => 'switch', 'default' => false, 'description' => '在所选分类页显示的标签筛选列表'],
|
||||||
|
['key' => 'tag_lists', 'label' => ' ', 'type' => 'fieldset', 'default' => ['lists' => [], 'dropdown' => false], 'dependsOn' => ['key' => 'tag_s', 'value' => true], 'fields' => [
|
||||||
|
['key' => 'lists', 'label' => '按钮列表', 'type' => 'async_select', 'source' => 'tags', 'multiple' => true, 'sortable' => true, 'default' => [], 'description' => '选择并排序需要显示的按钮'],
|
||||||
|
['key' => 'dropdown', 'label' => '下拉列表', 'type' => 'switch', 'default' => false, 'description' => '用下拉框显示更多内容'],
|
||||||
|
]],
|
||||||
|
['key' => 'orderby_s', 'label' => '排序方式', 'type' => 'switch', 'default' => true, 'description' => '在所选分类页显示排序方式按钮'],
|
||||||
|
['key' => 'orderby_lists', 'label' => ' ', 'type' => 'fieldset', 'default' => ['lists' => ['modified', 'views', 'like', 'comment_count'], 'dropdown' => false], 'dependsOn' => ['key' => 'orderby_s', 'value' => true], 'fields' => [
|
||||||
|
['key' => 'lists', 'label' => '显示排序方式', 'type' => 'select', 'multiple' => true, 'sortable' => true, 'default' => ['modified', 'views', 'like', 'comment_count'], 'options' => [
|
||||||
|
['label' => '更新', 'value' => 'modified'],
|
||||||
|
['label' => '发布', 'value' => 'date'],
|
||||||
|
['label' => '浏览', 'value' => 'views'],
|
||||||
|
['label' => '点赞', 'value' => 'like'],
|
||||||
|
['label' => '评论', 'value' => 'comment_count'],
|
||||||
|
['label' => '收藏', 'value' => 'favorite'],
|
||||||
|
['label' => '随机', 'value' => 'rand'],
|
||||||
|
], 'description' => '选择需要的排序方式按钮'],
|
||||||
|
['key' => 'dropdown', 'label' => '更多排序方式', 'type' => 'switch', 'default' => false, 'description' => '用下拉框显示全部排序方式'],
|
||||||
|
]],
|
||||||
|
// custom_filters 的 options 为运行时动态来源(custom_filter 维度定义),前端按 formData 解析。
|
||||||
|
['key' => 'custom_filters', 'label' => '自定义高级筛选', 'type' => 'checkbox', 'default' => [], 'options' => [], 'description' => '在所选分类页显示的自定义高级筛选项目。如果此处没有选项,或者您修改了上方的文章高级筛选明细,请先保存主题配置并刷新页面后再设置此处'],
|
||||||
]],
|
]],
|
||||||
]],
|
]],
|
||||||
],
|
],
|
||||||
|
|||||||
+65
-8
@@ -104,21 +104,54 @@ class TermMeta {
|
|||||||
$description = (string) $term->description;
|
$description = (string) $term->description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 高级筛选(参考主题 zib-category.php 语义):cat 页按 cat_custom_filter 全量覆盖
|
||||||
|
// 筛选项目,未匹配或 tag 页回退默认配置,custom_filter_show 决定自定义筛选显示。
|
||||||
|
$page = $taxonomy === 'post_tag' ? 'tag' : 'cat';
|
||||||
|
$custom_filter = class_exists('\AKSRT\Settings\Settings')
|
||||||
|
? \AKSRT\Settings\Settings::get_archive_custom_filter_config((int) $term->term_id, $page)
|
||||||
|
: ['custom_enabled' => false, 'custom_filters' => 'all', 'overrides' => []];
|
||||||
|
$overrides = is_array($custom_filter['overrides'] ?? null) ? $custom_filter['overrides'] : [];
|
||||||
|
|
||||||
|
$ajax_category_enabled = !empty($global['ajax_category_enabled']);
|
||||||
|
$ajax_category = is_array($global['ajax_category'] ?? null) ? $global['ajax_category'] : [];
|
||||||
|
$ajax_tag_enabled = !empty($global['ajax_tag_enabled']);
|
||||||
|
$ajax_tag = is_array($global['ajax_tag'] ?? null) ? $global['ajax_tag'] : [];
|
||||||
|
$orderby_enabled = !empty($global['orderby_enabled']);
|
||||||
|
$orderby_option = is_array($global['orderby_option'] ?? null) ? $global['orderby_option'] : [];
|
||||||
|
if (array_key_exists('cat_s', $overrides)) {
|
||||||
|
$ajax_category_enabled = !empty($overrides['cat_s']);
|
||||||
|
$ajax_category = is_array($overrides['cat_lists'] ?? null) ? $overrides['cat_lists'] : $ajax_category;
|
||||||
|
}
|
||||||
|
if (array_key_exists('tag_s', $overrides)) {
|
||||||
|
$ajax_tag_enabled = !empty($overrides['tag_s']);
|
||||||
|
$ajax_tag = is_array($overrides['tag_lists'] ?? null) ? $overrides['tag_lists'] : $ajax_tag;
|
||||||
|
}
|
||||||
|
if (array_key_exists('orderby_s', $overrides)) {
|
||||||
|
$orderby_enabled = !empty($overrides['orderby_s']);
|
||||||
|
$orderby_option = is_array($overrides['orderby_lists'] ?? null) ? $overrides['orderby_lists'] : $orderby_option;
|
||||||
|
}
|
||||||
|
$orderby = sanitize_key((string) ($orderby_option['lists'][0] ?? $global['orderby'] ?? 'date'));
|
||||||
|
|
||||||
|
$filter_all = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_post_filter_settings() : [];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'taxonomy' => $taxonomy,
|
'taxonomy' => $taxonomy,
|
||||||
'header_style' => $header_style,
|
'header_style' => $header_style,
|
||||||
'show_sidebar' => $sidebar_enabled,
|
'show_sidebar' => $sidebar_enabled,
|
||||||
'show_cover' => $header_style === 'cover' && !empty($global['cover_enabled']) && $cover_url !== '',
|
'show_cover' => $header_style === 'cover' && !empty($global['cover_enabled']) && $cover_url !== '',
|
||||||
'show_desc' => $taxonomy === 'post_tag' ? $description !== '' : (!empty($global['desc_enabled']) && $description !== ''),
|
'show_desc' => $taxonomy === 'post_tag' ? $description !== '' : (!empty($global['desc_enabled']) && $description !== ''),
|
||||||
'filter_bar' => !empty($global['filter_bar']),
|
'filter_bar' => !empty($global['filter_bar']) || $ajax_category_enabled || $ajax_tag_enabled || $orderby_enabled || !empty($custom_filter['custom_enabled']),
|
||||||
'post_count' => !empty($global['post_count']),
|
'post_count' => !empty($global['post_count']),
|
||||||
'orderby_enabled' => !empty($global['orderby_enabled']),
|
'orderby_enabled' => $orderby_enabled,
|
||||||
'orderby' => sanitize_key((string) ($global['orderby'] ?? 'date')),
|
'orderby' => $orderby,
|
||||||
'orderby_option'=> is_array($global['orderby_option'] ?? null) ? $global['orderby_option'] : [],
|
'orderby_option'=> $orderby_option,
|
||||||
'ajax_category_enabled' => !empty($global['ajax_category_enabled']),
|
'ajax_category_enabled' => $ajax_category_enabled,
|
||||||
'ajax_tag_enabled' => !empty($global['ajax_tag_enabled']),
|
'ajax_tag_enabled' => $ajax_tag_enabled,
|
||||||
'ajax_category' => is_array($global['ajax_category'] ?? null) ? $global['ajax_category'] : [],
|
'ajax_category' => $ajax_category,
|
||||||
'ajax_tag' => is_array($global['ajax_tag'] ?? null) ? $global['ajax_tag'] : [],
|
'ajax_tag' => $ajax_tag,
|
||||||
|
'custom_filter_enabled' => !empty($custom_filter['custom_enabled']),
|
||||||
|
'custom_filter_keys' => $custom_filter['custom_filters'] ?? 'all',
|
||||||
|
'custom_dimensions' => is_array($filter_all['custom_filter'] ?? null) ? $filter_all['custom_filter'] : [],
|
||||||
'per_page' => max(4, min(48, (int) ($global['per_page'] ?? 12))),
|
'per_page' => max(4, min(48, (int) ($global['per_page'] ?? 12))),
|
||||||
'cover_url' => $cover_url,
|
'cover_url' => $cover_url,
|
||||||
'description' => $description,
|
'description' => $description,
|
||||||
@@ -178,6 +211,30 @@ class TermMeta {
|
|||||||
$query->set('tax_query', $tax_query);
|
$query->set('tax_query', $tax_query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 自定义高级筛选维度 meta 过滤(参考主题 zib_sift_posts_per_page:meta_query LIKE;
|
||||||
|
// meta 值为序列化数组,用带引号精确匹配避免前缀误命中)。
|
||||||
|
if (!empty($settings['custom_filter_enabled'])) {
|
||||||
|
$keys = $settings['custom_filter_keys'];
|
||||||
|
$meta_query = is_array($query->get('meta_query')) ? $query->get('meta_query') : [];
|
||||||
|
foreach ((array) ($settings['custom_dimensions'] ?? []) as $dimension) {
|
||||||
|
$dim_key = sanitize_key((string) ($dimension['key'] ?? ''));
|
||||||
|
if ($dim_key === '' || empty($_GET[$dim_key])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($keys !== 'all' && !in_array($dim_key, (array) $keys, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$meta_query[] = [
|
||||||
|
'key' => '_aksrt_cf_' . $dim_key,
|
||||||
|
'value' => '"' . sanitize_key(wp_unslash((string) $_GET[$dim_key])) . '"',
|
||||||
|
'compare' => 'LIKE',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if ($meta_query !== []) {
|
||||||
|
$query->set('meta_query', $meta_query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$orderby = isset($_GET['orderby']) ? sanitize_key(wp_unslash((string) $_GET['orderby'])) : 'date';
|
$orderby = isset($_GET['orderby']) ? sanitize_key(wp_unslash((string) $_GET['orderby'])) : 'date';
|
||||||
$allowed = class_exists('\AKSRT\Settings\Settings')
|
$allowed = class_exists('\AKSRT\Settings\Settings')
|
||||||
? \AKSRT\Settings\Settings::homepage_allowed_sorts()
|
? \AKSRT\Settings\Settings::homepage_allowed_sorts()
|
||||||
|
|||||||
@@ -231,10 +231,9 @@ trait DisplayPageGroups {
|
|||||||
'list' => $list,
|
'list' => $list,
|
||||||
'thumb' => $thumb,
|
'thumb' => $thumb,
|
||||||
'filter' => [
|
'filter' => [
|
||||||
'filter_enabled' => $filter['enabled'],
|
'custom_filter_show' => $filter['custom_filter_show'],
|
||||||
'filter_categories' => $filter['categories'],
|
'custom_filter' => $filter['custom_filter'],
|
||||||
'filter_tags' => $filter['tags'],
|
'cat_custom_filter' => $filter['cat_custom_filter'],
|
||||||
'filter_sort' => $filter['sort'],
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,34 +104,46 @@ class PostDisplay {
|
|||||||
$settings = Settings::get_all();
|
$settings = Settings::get_all();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'enabled' => !empty($settings['filter_enabled']),
|
'custom_filter_show' => is_array($settings['custom_filter_show'] ?? null) ? array_values(array_intersect(array_map('sanitize_key', $settings['custom_filter_show']), ['cat', 'tag'])) : [],
|
||||||
'categories' => !isset($settings['filter_categories']) || !empty($settings['filter_categories']),
|
|
||||||
'tags' => !empty($settings['filter_tags']),
|
|
||||||
'sort' => !isset($settings['filter_sort']) || !empty($settings['filter_sort']),
|
|
||||||
'ajax_enabled' => !empty($settings['filter_ajax_enabled']),
|
|
||||||
'cat_show_cat' => !isset($settings['filter_cat_show_cat']) || !empty($settings['filter_cat_show_cat']),
|
|
||||||
'cat_show_tag' => !empty($settings['filter_cat_show_tag']),
|
|
||||||
'tag_show_cat' => !isset($settings['filter_tag_show_cat']) || !empty($settings['filter_tag_show_cat']),
|
|
||||||
'tag_show_tag' => !empty($settings['filter_tag_show_tag']),
|
|
||||||
'custom_filter_show' => is_array($settings['custom_filter_show'] ?? null) ? array_map('sanitize_key', $settings['custom_filter_show']) : ['cat', 'tag'],
|
|
||||||
'custom_filter' => self::sanitize_custom_filter_defs($settings['custom_filter'] ?? []),
|
'custom_filter' => self::sanitize_custom_filter_defs($settings['custom_filter'] ?? []),
|
||||||
'cat_custom_filter' => self::sanitize_cat_custom_filter($settings['cat_custom_filter'] ?? []),
|
'cat_custom_filter' => self::sanitize_cat_custom_filter($settings['cat_custom_filter'] ?? []),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get_filter_config_for_category(int $term_id): array {
|
/**
|
||||||
|
* 归档页高级筛选配置(参考主题 zib-category.php:zib_get_ajax_option_menu 语义)。
|
||||||
|
*
|
||||||
|
* cat 页优先匹配 cat_custom_filter(绑定 cats 含当前 term 时全量覆盖筛选项目);
|
||||||
|
* 未匹配或 tag 页回退默认配置:custom_filter_show 决定是否显示自定义筛选('all')。
|
||||||
|
*
|
||||||
|
* @return array{custom_enabled:bool,custom_filters:mixed,overrides:array<string,mixed>}
|
||||||
|
*/
|
||||||
|
public static function get_archive_custom_filter_config(int $term_id, string $page): array {
|
||||||
$cfg = self::get_post_filter_settings();
|
$cfg = self::get_post_filter_settings();
|
||||||
foreach ($cfg['cat_custom_filter'] as $rule) {
|
|
||||||
if (in_array($term_id, $rule['cats'], true)) {
|
if ($page === 'cat') {
|
||||||
return $rule;
|
foreach ($cfg['cat_custom_filter'] as $rule) {
|
||||||
|
if (in_array($term_id, $rule['cats'], true)) {
|
||||||
|
return [
|
||||||
|
'custom_enabled' => $rule['custom_filters'] !== [],
|
||||||
|
'custom_filters' => $rule['custom_filters'],
|
||||||
|
'overrides' => [
|
||||||
|
'cat_s' => $rule['cat_s'],
|
||||||
|
'cat_lists' => $rule['cat_lists'],
|
||||||
|
'tag_s' => $rule['tag_s'],
|
||||||
|
'tag_lists' => $rule['tag_lists'],
|
||||||
|
'orderby_s' => $rule['orderby_s'],
|
||||||
|
'orderby_lists' => $rule['orderby_lists'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'cats' => [],
|
'custom_enabled' => in_array($page, $cfg['custom_filter_show'], true),
|
||||||
'cat_s' => $cfg['cat_show_cat'],
|
'custom_filters' => 'all',
|
||||||
'tag_s' => $cfg['cat_show_tag'],
|
'overrides' => [],
|
||||||
'orderby_s' => $cfg['sort'],
|
|
||||||
'custom_filters' => [],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,16 +518,49 @@ class PostDisplay {
|
|||||||
if (!is_array($item)) continue;
|
if (!is_array($item)) continue;
|
||||||
$cats = is_array($item['cats'] ?? null) ? array_values(array_filter(array_map('absint', $item['cats']), static fn($id) => $id > 0)) : [];
|
$cats = is_array($item['cats'] ?? null) ? array_values(array_filter(array_map('absint', $item['cats']), static fn($id) => $id > 0)) : [];
|
||||||
if (empty($cats)) continue;
|
if (empty($cats)) continue;
|
||||||
$filters_raw = trim((string) ($item['custom_filters'] ?? ''));
|
|
||||||
$filters = $filters_raw === '' ? [] : array_values(array_filter(array_map('sanitize_key', preg_split('/[,,\s]+/', $filters_raw) ?: [])));
|
|
||||||
$result[] = [
|
$result[] = [
|
||||||
'cats' => $cats,
|
'cats' => $cats,
|
||||||
'cat_s' => !isset($item['cat_s']) || !empty($item['cat_s']),
|
'cat_s' => !empty($item['cat_s']),
|
||||||
|
'cat_lists' => self::sanitize_filter_option_lists($item['cat_lists'] ?? []),
|
||||||
'tag_s' => !empty($item['tag_s']),
|
'tag_s' => !empty($item['tag_s']),
|
||||||
'orderby_s' => !isset($item['orderby_s']) || !empty($item['orderby_s']),
|
'tag_lists' => self::sanitize_filter_option_lists($item['tag_lists'] ?? []),
|
||||||
'custom_filters' => $filters,
|
'orderby_s' => !empty($item['orderby_s']),
|
||||||
|
'orderby_lists' => self::sanitize_filter_orderby_lists($item['orderby_lists'] ?? []),
|
||||||
|
'custom_filters' => is_array($item['custom_filters'] ?? null)
|
||||||
|
? array_values(array_unique(array_filter(array_map('sanitize_key', $item['custom_filters']))))
|
||||||
|
: [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 归档筛选按钮列表 fieldset(lists + dropdown,参考主题 CFS_Module::ajax_but)。
|
||||||
|
*/
|
||||||
|
private static function sanitize_filter_option_lists($raw): array {
|
||||||
|
$option = is_array($raw) ? $raw : [];
|
||||||
|
$lists = is_array($option['lists'] ?? null) ? array_values(array_filter(array_map('absint', $option['lists']), static fn($id) => $id > 0)) : [];
|
||||||
|
return [
|
||||||
|
'lists' => $lists,
|
||||||
|
'dropdown' => !empty($option['dropdown']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序方式 fieldset(lists + dropdown,参考主题 CFS_Module::orderby)。
|
||||||
|
*/
|
||||||
|
private static function sanitize_filter_orderby_lists($raw): array {
|
||||||
|
$option = is_array($raw) ? $raw : [];
|
||||||
|
$allowed = Settings::homepage_allowed_sorts();
|
||||||
|
$lists = is_array($option['lists'] ?? null)
|
||||||
|
? array_values(array_intersect(array_map('sanitize_key', $option['lists']), $allowed))
|
||||||
|
: [];
|
||||||
|
if ($lists === []) {
|
||||||
|
$lists = ['modified', 'views', 'like', 'comment_count'];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'lists' => $lists,
|
||||||
|
'dropdown' => !empty($option['dropdown']),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -666,9 +666,9 @@ class Settings {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post-list filter toolbar settings.
|
* Post-list advanced filter settings (reference parity).
|
||||||
*
|
*
|
||||||
* @return array{enabled:bool,categories:bool,tags:bool,sort:bool}
|
* @return array{custom_filter_show:array,custom_filter:array,cat_custom_filter:array}
|
||||||
*/
|
*/
|
||||||
public static function get_post_filter_settings(): array {
|
public static function get_post_filter_settings(): array {
|
||||||
return PostDisplay::get_post_filter_settings();
|
return PostDisplay::get_post_filter_settings();
|
||||||
@@ -677,14 +677,14 @@ class Settings {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get custom filter definitions for a specific category.
|
* Archive custom filter config (cat_custom_filter override + custom_filter_show gate).
|
||||||
* Finds the matching cat_custom_filter binding, falls back to global custom_filter.
|
|
||||||
*
|
*
|
||||||
* @param int $cat_id Category term ID.
|
* @param int $term_id Category term ID.
|
||||||
* @return array{dimensions: array, show_cat: bool, show_tag: bool, show_orderby: bool}
|
* @param string $page cat|tag.
|
||||||
|
* @return array{custom_enabled:bool,custom_filters:mixed,overrides:array<string,mixed>}
|
||||||
*/
|
*/
|
||||||
public static function get_filter_config_for_category(int $cat_id): array {
|
public static function get_archive_custom_filter_config(int $term_id, string $page): array {
|
||||||
return PostDisplay::get_filter_config_for_category($cat_id);
|
return PostDisplay::get_archive_custom_filter_config($term_id, $page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,16 @@ if (!empty($archive_settings['ajax_tag_enabled']) && function_exists('aksrt_arch
|
|||||||
$filter_groups[] = $group;
|
$filter_groups[] = $group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!empty($archive_settings['custom_filter_enabled']) && function_exists('aksrt_archive_build_custom_filter_groups')) {
|
||||||
|
$filter_groups = array_merge(
|
||||||
|
$filter_groups,
|
||||||
|
aksrt_archive_build_custom_filter_groups(
|
||||||
|
(array) ($archive_settings['custom_dimensions'] ?? []),
|
||||||
|
$archive_settings['custom_filter_keys'] ?? 'all',
|
||||||
|
$ajax_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (function_exists('aksrt_archive_open_shell')) {
|
if (function_exists('aksrt_archive_open_shell')) {
|
||||||
aksrt_archive_open_shell([
|
aksrt_archive_open_shell([
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
/**
|
||||||
|
* 高级筛选设置页参考主题 1:1 契约。
|
||||||
|
*
|
||||||
|
* 参考:zibll inc/options/admin-options.php「高级筛选」区块(5251-5424 行)与
|
||||||
|
* 前台链路(zib-category.php zib_get_ajax_option_menu / zib_get_option_custom_filter_but、
|
||||||
|
* zib-post.php zib_sift_posts_per_page)。
|
||||||
|
*/
|
||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const ROOT = path.resolve(__dirname, '..', '..');
|
||||||
|
const read = (rel) => fs.readFileSync(path.join(ROOT, rel), 'utf8');
|
||||||
|
|
||||||
|
const js = read('assets/admin/src/schema/sections/post-list/filter.js');
|
||||||
|
const php = read('src/Admin/Schema/SchemaPostList.php');
|
||||||
|
const postDisplay = read('src/Settings/PostDisplay.php');
|
||||||
|
const settings = read('src/Settings/Settings.php');
|
||||||
|
const termMeta = read('src/Core/TermMeta.php');
|
||||||
|
const filtersHelper = read('inc/helpers/aksrt-archive-filters.php');
|
||||||
|
const categoryTpl = read('category.php');
|
||||||
|
const tagTpl = read('tag.php');
|
||||||
|
const archiveJs = read('assets/js/core/archive.js');
|
||||||
|
const globalController = read('src/Admin/GlobalController.php');
|
||||||
|
|
||||||
|
// 参考主题字段键(顺序即参考主题定义顺序)。
|
||||||
|
const REFERENCE_KEYS = ['custom_filter_show', 'custom_filter', 'cat_custom_filter'];
|
||||||
|
// AKSRT 私有旧字段(参考主题无此配置,须移除避免双路)。
|
||||||
|
const LEGACY_KEYS = [
|
||||||
|
'filter_enabled',
|
||||||
|
'filter_categories',
|
||||||
|
'filter_tags',
|
||||||
|
'filter_sort',
|
||||||
|
'filter_ajax_enabled',
|
||||||
|
'filter_cat_show_cat',
|
||||||
|
'filter_cat_show_tag',
|
||||||
|
'filter_tag_show_cat',
|
||||||
|
'filter_tag_show_tag',
|
||||||
|
];
|
||||||
|
// 参考主题字段中本项目不做的部分(无 topics 专题分类法)。
|
||||||
|
const EXCLUDED_KEYS = ['topics_s', 'topics_lists'];
|
||||||
|
|
||||||
|
test('JS schema 字段键与顺序对齐参考主题', () => {
|
||||||
|
const found = [...js.matchAll(/key:\s*'([a-z0-9_]+)',\s*(?:label|type):/g)].map((m) => m[1]);
|
||||||
|
// 顶层字段顺序(content 提示行与嵌套子字段除外)。
|
||||||
|
for (const key of REFERENCE_KEYS) {
|
||||||
|
assert.ok(found.includes(key), `JS schema 缺少字段 ${key}`);
|
||||||
|
}
|
||||||
|
const topIdx = REFERENCE_KEYS.map((k) => js.indexOf(`key: '${k}'`));
|
||||||
|
assert.ok(topIdx.every((v, i) => i === 0 || v > topIdx[i - 1]), '顶层字段顺序应符合参考主题');
|
||||||
|
// 嵌套子字段。
|
||||||
|
for (const sub of ['name', 'key', 'vals']) {
|
||||||
|
assert.ok(new RegExp(`key:\\s*'${sub}',\\s*label:`).test(js), `custom_filter 缺少子字段 ${sub}`);
|
||||||
|
}
|
||||||
|
const catSub = js.slice(js.indexOf("key: 'cat_custom_filter'"));
|
||||||
|
const catOrder = ['cats', 'cat_s', 'cat_lists', 'tag_s', 'tag_lists', 'orderby_s', 'orderby_lists', 'custom_filters'];
|
||||||
|
const catIdx = catOrder.map((k) => catSub.indexOf(`key: '${k}'`));
|
||||||
|
assert.ok(catIdx.every((v) => v !== -1), 'cat_custom_filter 缺少子字段');
|
||||||
|
assert.ok(catIdx.every((v, i) => i === 0 || v > catIdx[i - 1]), 'cat_custom_filter 子字段顺序应符合参考主题');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('PHP schema 字段键与顺序对齐参考主题', () => {
|
||||||
|
const filterSection = php.slice(php.indexOf("'filter' => ["));
|
||||||
|
assert.ok(filterSection.includes("'title' => '高级筛选'"), 'PHP filter 区块缺失');
|
||||||
|
assert.ok(filterSection.includes("'version' => '2.0.0'"), 'PHP filter 区块版本应为 2.0.0');
|
||||||
|
const order = REFERENCE_KEYS.map((k) => filterSection.indexOf(`'${k}'`));
|
||||||
|
assert.ok(order.every((v) => v !== -1), `PHP schema 缺少字段: ${REFERENCE_KEYS.filter((k) => order.indexOf(-1) === -1)}`);
|
||||||
|
assert.ok(order.every((v, i) => i === 0 || v > order[i - 1]), 'PHP 字段顺序应符合参考主题');
|
||||||
|
const catOrder = ['cats', 'cat_s', 'cat_lists', 'tag_s', 'tag_lists', 'orderby_s', 'orderby_lists', 'custom_filters'];
|
||||||
|
const catSub = filterSection.slice(filterSection.indexOf("'cat_custom_filter'"));
|
||||||
|
const catIdx = catOrder.map((k) => catSub.indexOf(`'${k}'`));
|
||||||
|
assert.ok(catIdx.every((v) => v !== -1), 'PHP cat_custom_filter 缺少子字段');
|
||||||
|
assert.ok(catIdx.every((v, i) => i === 0 || v > catIdx[i - 1]), 'PHP cat_custom_filter 子字段顺序应符合参考主题');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('JS schema 默认值、选项与依赖对齐参考主题', () => {
|
||||||
|
assert.match(js, /custom_filter_show:\s*\[\]/);
|
||||||
|
assert.match(js, /custom_filter:\s*\[\]/);
|
||||||
|
assert.match(js, /cat_custom_filter:\s*\[\]/);
|
||||||
|
for (const option of ['分类页面显示', '标签页面显示']) {
|
||||||
|
assert.ok(js.includes(option), `custom_filter_show 缺少选项 ${option}`);
|
||||||
|
}
|
||||||
|
// 排序方式默认值与参考主题一致(orderby() default)。
|
||||||
|
assert.match(js, /default:\s*\[\s*'modified',\s*'views',\s*'like',\s*'comment_count'\s*\]/);
|
||||||
|
for (const option of ['更新', '发布', '浏览', '点赞', '评论', '收藏', '随机']) {
|
||||||
|
assert.ok(js.includes(`label: '${option}'`), `排序方式缺少选项 ${option}`);
|
||||||
|
}
|
||||||
|
// 依赖链(参考主题 dependency)。
|
||||||
|
assert.match(js, /key: 'cat_lists',[\s\S]{0,200}?dependsOn:\s*\{\s*key:\s*'cat_s',\s*value:\s*true\s*\}/);
|
||||||
|
assert.match(js, /key: 'tag_lists',[\s\S]{0,200}?dependsOn:\s*\{\s*key:\s*'tag_s',\s*value:\s*true\s*\}/);
|
||||||
|
assert.match(js, /key: 'orderby_lists',[\s\S]{0,220}?dependsOn:\s*\{\s*key:\s*'orderby_s',\s*value:\s*true\s*\}/);
|
||||||
|
// custom_filters 为动态选项 checkbox(引用 custom_filter 维度定义)。
|
||||||
|
assert.match(js, /key: 'custom_filters',[\s\S]{0,400}?options:\s*'custom_filter'/);
|
||||||
|
// 提示行承载参考主题 submessage 与官网教程链接。
|
||||||
|
assert.match(js, /type:\s*'content'/);
|
||||||
|
assert.match(js, /ui_only:\s*true/);
|
||||||
|
assert.match(js, /zibll\.com\/15886\.html/);
|
||||||
|
assert.match(js, /避免与其他mate_key重复/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('旧字段与 topics 在 schema 中均已移除(无双路)', () => {
|
||||||
|
for (const legacy of LEGACY_KEYS) {
|
||||||
|
assert.ok(!js.includes(legacy), `JS schema 残留旧字段: ${legacy}`);
|
||||||
|
assert.ok(!php.includes(legacy), `PHP schema 残留旧字段: ${legacy}`);
|
||||||
|
assert.ok(!postDisplay.includes(`$settings['${legacy}']`), `PostDisplay 残留旧字段读取: ${legacy}`);
|
||||||
|
}
|
||||||
|
for (const excluded of EXCLUDED_KEYS) {
|
||||||
|
assert.ok(!js.includes(excluded), `JS schema 不应包含 ${excluded}(本项目无专题分类法)`);
|
||||||
|
assert.ok(!php.includes(excluded), `PHP schema 不应包含 ${excluded}`);
|
||||||
|
}
|
||||||
|
// 运行时只读新键。
|
||||||
|
assert.match(postDisplay, /\$settings\['custom_filter_show'\]/);
|
||||||
|
assert.match(postDisplay, /\$settings\['custom_filter'\]/);
|
||||||
|
assert.match(postDisplay, /\$settings\['cat_custom_filter'\]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('JS schema 与 PHP schema 一致性', () => {
|
||||||
|
// JS 持久化字段 = 参考字段集;PHP 同样包含全部参考字段。
|
||||||
|
for (const key of REFERENCE_KEYS) {
|
||||||
|
assert.ok(php.includes(`'${key}'`), `PHP schema 缺少 ${key}`);
|
||||||
|
}
|
||||||
|
// addText 与参考主题 button_title 一致。
|
||||||
|
assert.match(js, /addText:\s*'添加筛选类别'/);
|
||||||
|
assert.match(js, /addText:\s*'添加值'/);
|
||||||
|
assert.match(js, /addText:\s*'添加自定义分类筛选'/);
|
||||||
|
assert.match(php, /'addText'\s*=>\s*'添加筛选类别'/);
|
||||||
|
assert.match(php, /'addText'\s*=>\s*'添加值'/);
|
||||||
|
assert.match(php, /'addText'\s*=>\s*'添加自定义分类筛选'/);
|
||||||
|
// 排序选项不含 zibpay 系列与销量(本项目无对应运行时)。
|
||||||
|
assert.ok(!js.includes('zibpay_price') && !js.includes('sales_volume'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('前台链路:覆盖与门禁(参考主题 zib-category.php 语义)', () => {
|
||||||
|
// cat 页按 cat_custom_filter 覆盖,回退 custom_filter_show 门禁。
|
||||||
|
assert.match(postDisplay, /function get_archive_custom_filter_config\(int \$term_id, string \$page\)/);
|
||||||
|
assert.match(postDisplay, /if \(\$page === 'cat'\)/);
|
||||||
|
assert.match(postDisplay, /in_array\(\$term_id, \$rule\['cats'\], true\)/);
|
||||||
|
assert.match(postDisplay, /in_array\(\$page, \$cfg\['custom_filter_show'\], true\)/);
|
||||||
|
assert.match(postDisplay, /'custom_filters'\s*=>\s*'all'/);
|
||||||
|
// Settings 委托。
|
||||||
|
assert.match(settings, /function get_archive_custom_filter_config\(int \$term_id, string \$page\)/);
|
||||||
|
// 归档设置接入覆盖与自定义筛选配置。
|
||||||
|
assert.match(termMeta, /get_archive_custom_filter_config\(/);
|
||||||
|
assert.match(termMeta, /'custom_filter_enabled'/);
|
||||||
|
assert.match(termMeta, /'custom_filter_keys'/);
|
||||||
|
assert.match(termMeta, /'custom_dimensions'/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('前台链路:维度筛选渲染与查询过滤', () => {
|
||||||
|
// 渲染:维度 chip 组(全部 chip 参考主题 focus 语义)。
|
||||||
|
assert.match(filtersHelper, /function aksrt_archive_build_custom_filter_groups\(/);
|
||||||
|
assert.match(filtersHelper, /data-filter-dim/);
|
||||||
|
assert.match(filtersHelper, /\$keys !== 'all' && !in_array\(\$dim_key, \(array\) \$keys, true\)/);
|
||||||
|
assert.match(categoryTpl, /aksrt_archive_build_custom_filter_groups\(/);
|
||||||
|
assert.match(tagTpl, /aksrt_archive_build_custom_filter_groups\(/);
|
||||||
|
// 查询:_aksrt_cf_{key} meta_query LIKE(主查询 + AJAX 端点)。
|
||||||
|
assert.match(termMeta, /'_aksrt_cf_' \. \$dim_key/);
|
||||||
|
assert.match(termMeta, /'compare'\s*=>\s*'LIKE'/);
|
||||||
|
assert.match(globalController, /'custom_filter'\s*=>\s*\['type' => 'string'/);
|
||||||
|
assert.match(globalController, /json_decode\(\$custom_raw, true\)/);
|
||||||
|
assert.match(globalController, /'_aksrt_cf_' \. \$dim_key/);
|
||||||
|
// JS:维度 chip 状态、请求参数与 URL 同步。
|
||||||
|
assert.match(archiveJs, /data-filter-dim/);
|
||||||
|
assert.match(archiveJs, /JSON\.stringify\(state\.custom\)/);
|
||||||
|
assert.match(archiveJs, /state\.custom\[dimKey\]/);
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"generated_at": "2026-09-24T06:24:30+00:00",
|
"generated_at": "2026-09-24T07:38:16+00:00",
|
||||||
"errors": [],
|
"errors": [],
|
||||||
"warnings": [
|
"warnings": [
|
||||||
"customPage tools-update section 'tools/update' not in PHP schema (ok if pure operational page)",
|
"customPage tools-update section 'tools/update' not in PHP schema (ok if pure operational page)",
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
"enum incomplete parse for 'list_show_type' (php subset of js): php=[separate] js=[separate,no_margin]",
|
"enum incomplete parse for 'list_show_type' (php subset of js): php=[separate] js=[separate,no_margin]",
|
||||||
"enum incomplete parse for 'list_thumb_fit' (php subset of js): php=[] js=[,contain]",
|
"enum incomplete parse for 'list_thumb_fit' (php subset of js): php=[] js=[,contain]",
|
||||||
"enum incomplete parse for 'list_type' (php subset of js): php=[text] js=[text,thumb,thumb_if_has,card]",
|
"enum incomplete parse for 'list_type' (php subset of js): php=[text] js=[text,thumb,thumb_if_has,card]",
|
||||||
|
"enum incomplete parse for 'lists' (php subset of js): php=[modified] js=[modified,date,views,like,comment_count,favorite,rand]",
|
||||||
"enum incomplete parse for 'mandatory_bind' (php subset of js): php=[email] js=[email,phone,email_phone,close]",
|
"enum incomplete parse for 'mandatory_bind' (php subset of js): php=[email] js=[email,phone,email_phone,close]",
|
||||||
"enum incomplete parse for 'message_paginate_type' (php subset of js): php=[ajax_lists] js=[ajax_lists,default]",
|
"enum incomplete parse for 'message_paginate_type' (php subset of js): php=[ajax_lists] js=[ajax_lists,default]",
|
||||||
"enum incomplete parse for 'oauth_bind_type' (php subset of js): php=[auto] js=[auto,manual]",
|
"enum incomplete parse for 'oauth_bind_type' (php subset of js): php=[auto] js=[auto,manual]",
|
||||||
@@ -234,7 +235,7 @@
|
|||||||
"entry": true,
|
"entry": true,
|
||||||
"missing_chunks": [],
|
"missing_chunks": [],
|
||||||
"chunks": [
|
"chunks": [
|
||||||
"SchemaPage.CPZNRYWC.js",
|
"SchemaPage.QsKL1FOA.js",
|
||||||
"vendor-vue.BYzo-2I-.js",
|
"vendor-vue.BYzo-2I-.js",
|
||||||
"FaIcon.B3OOcphx.js",
|
"FaIcon.B3OOcphx.js",
|
||||||
"FaIconPicker.DHrC0Y72.js",
|
"FaIconPicker.DHrC0Y72.js",
|
||||||
@@ -249,8 +250,8 @@
|
|||||||
"vendor-draggable.YNevSrwY.js",
|
"vendor-draggable.YNevSrwY.js",
|
||||||
"PageHeader.jmNZVzHG.js",
|
"PageHeader.jmNZVzHG.js",
|
||||||
"FooterTabbarSettings.CwSsjNlJ.js",
|
"FooterTabbarSettings.CwSsjNlJ.js",
|
||||||
"PostPageSettings.DxiP1RzY.js",
|
"PostPageSettings.DBCiH7_B.js",
|
||||||
"CommentSettings.C6xJZ_dU.js",
|
"CommentSettings.DwLb53BZ.js",
|
||||||
"PermissionsCapabilities.BeLUij9c.js",
|
"PermissionsCapabilities.BeLUij9c.js",
|
||||||
"CmsCredits.CTDJTkG-.js",
|
"CmsCredits.CTDJTkG-.js",
|
||||||
"ThemeInfoPage.CbJKzVFa.js",
|
"ThemeInfoPage.CbJKzVFa.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user