Files
wordpress-theme-aksrt/assets/admin/src/App.vue
T
kate522andClaude Opus 5 35bb96e82f refactor: rename the theme brand and every identifier from AKSRT to Jio
Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.

Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
  cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
  window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)

Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
  so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
  (dropped 3 stale entries, added the 17 missing ones)

Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
  get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
  when it is empty

Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.

Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-01 20:05:04 +08:00

363 lines
10 KiB
Vue

<template>
<n-config-provider
:style-mount-target="styleMountTarget"
:theme-overrides="adminThemeOverrides"
>
<n-message-provider>
<n-dialog-provider>
<n-notification-provider>
<n-layout v-if="!isMobile" class="jio-admin-layout" has-sider>
<n-layout-sider
class="jio-admin-sider"
bordered
collapse-mode="width"
:collapsed-width="64"
:width="232"
show-collapse-trigger
:collapsed="collapsed"
:native-scrollbar="false"
@collapse="collapsed = true"
@expand="collapsed = false"
>
<div class="jio-logo">
<span class="logo-icon">AK</span>
<span v-if="!collapsed" class="jio-logo__copy">
<span class="logo-text">Jio</span>
<span class="jio-logo__version">v{{ themeVersion }}</span>
</span>
</div>
<n-menu
class="jio-admin-menu"
:value="activeMenuKey"
:collapsed="collapsed"
:collapsed-width="64"
:options="menuOptions"
@update:value="handleMenuClick"
/>
</n-layout-sider>
<n-layout :native-scrollbar="false" class="jio-admin-content">
<div class="jio-admin-content__inner">
<router-view v-slot="{ Component }">
<transition name="fade-slide" mode="out-in">
<div class="jio-page-wrapper" :key="route.fullPath">
<component :is="Component" />
</div>
</transition>
</router-view>
<div v-if="pageError" class="jio-error-fallback">
<p class="jio-error-title">页面渲染异常</p>
<p class="jio-error-msg">{{ pageError }}</p>
<n-button size="small" @click="pageError = null">重试</n-button>
</div>
</div>
</n-layout>
</n-layout>
<div v-else class="jio-admin-mobile">
<MobileTopBar :title="currentPageTitle" @toggle="drawerActive = true" />
<n-drawer v-model:show="drawerActive" placement="left" :width="280">
<n-drawer-content :native-scrollbar="false" body-content-style="padding:0;">
<div class="jio-logo jio-logo--drawer">
<span class="logo-icon">AK</span>
<span class="jio-logo__copy">
<span class="logo-text">Jio</span>
<span class="jio-logo__version">v{{ themeVersion }}</span>
</span>
</div>
<n-menu
class="jio-admin-menu"
:value="activeMenuKey"
:options="menuOptions"
@update:value="handleMenuClick"
/>
</n-drawer-content>
</n-drawer>
<div class="jio-admin-mobile__body">
<router-view v-slot="{ Component }">
<transition name="fade-slide" mode="out-in">
<div class="jio-page-wrapper" :key="route.fullPath">
<component :is="Component" />
</div>
</transition>
</router-view>
<div v-if="pageError" class="jio-error-fallback">
<p class="jio-error-title">页面渲染异常</p>
<p class="jio-error-msg">{{ pageError }}</p>
<n-button size="small" @click="pageError = null">重试</n-button>
</div>
</div>
</div>
</n-notification-provider>
</n-dialog-provider>
</n-message-provider>
</n-config-provider>
</template>
<script setup>
import { computed, onBeforeUnmount, onErrorCaptured, onMounted, provide, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { NConfigProvider } from 'naive-ui'
import MobileTopBar from './components/MobileTopBar.vue'
import { styleMountTarget } from './style-target'
import { ADMIN_SECTIONS } from './constants/admin-sections'
const router = useRouter()
const route = useRoute()
const collapsed = ref(false)
const pageError = ref(null)
const isMobile = ref(false)
const drawerActive = ref(false)
const themeVersion = window.jioAdminData?.themeVersion || window.jioAdminData?.version || '9.0'
const MOBILE_BREAKPOINT = 960
const adminThemeOverrides = {
common: {
primaryColor: '#18A058',
primaryColorHover: '#36AD6A',
primaryColorPressed: '#0C7A43',
primaryColorSuppl: '#36AD6A',
bodyColor: '#F5F7F6',
cardColor: '#FFFFFF',
modalColor: '#FFFFFF',
popoverColor: '#FFFFFF',
textColorBase: '#1F2923',
textColor1: '#1F2923',
textColor2: '#465149',
textColor3: '#6F7A72',
borderColor: '#E2E8E3',
dividerColor: '#E2E8E3',
borderRadius: '8px',
borderRadiusSmall: '6px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif',
},
Button: {
borderRadiusMedium: '8px',
heightMedium: '36px',
fontWeight: '600',
},
Card: {
borderRadius: '12px',
borderColor: '#E2E8E3',
},
DataTable: {
thColor: '#F7F9F7',
thTextColor: '#465149',
tdColorHover: '#F7FBF8',
borderColor: '#E2E8E3',
},
Input: {
borderRadius: '8px',
heightMedium: '36px',
},
Menu: {
itemColorActive: '#EAF7EF',
itemColorActiveHover: '#EAF7EF',
itemColorHover: '#F2F8F4',
itemTextColorActive: '#168A4B',
itemTextColorActiveHover: '#168A4B',
itemTextColorHover: '#168A4B',
},
Switch: {
railColorActive: '#18A058',
},
}
function checkMobile() {
isMobile.value = window.innerWidth < MOBILE_BREAKPOINT
}
onMounted(() => {
checkMobile()
window.addEventListener('resize', checkMobile)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', checkMobile)
})
provide('collapsed', collapsed)
onErrorCaptured((err) => {
console.error('[Jio] 组件渲染错误:', err)
pageError.value = err?.message || String(err)
return false
})
/* ------------------------------------------------------------------ */
/* Build key → path lookup from ADMIN_SECTIONS */
/* ------------------------------------------------------------------ */
const keyToPath = {}
for (const section of ADMIN_SECTIONS) {
if (section.children) {
for (const child of section.children) {
keyToPath[child.key] = child.route
}
}
// For placeholder sections (no children), the section key maps to its route
if (section.placeholder) {
keyToPath[section.key] = section.route
}
}
/* ------------------------------------------------------------------ */
/* Build NaiveUI menu options from ADMIN_SECTIONS */
/* ------------------------------------------------------------------ */
const menuOptions = ADMIN_SECTIONS.filter((section) => !section.hidden).map((section) => {
if (section.placeholder) {
// Placeholder sections have no children — render as a leaf menu item
return {
label: section.label,
key: section.key,
icon: section.icon,
}
}
return {
label: section.label,
key: section.key,
icon: section.icon,
children: section.children.map((child) => ({
label: child.secondaryEntry ? `${child.label} · 次级` : child.label,
key: child.key,
props: child.secondaryEntry ? { class: 'jio-menu-item--secondary' } : undefined,
})),
}
})
/* ------------------------------------------------------------------ */
/* Active menu key resolution */
/* ------------------------------------------------------------------ */
const activeMenuKey = computed(() => {
const path = route.path
// Find the child whose route matches the current path
for (const section of ADMIN_SECTIONS) {
if (section.children) {
for (const child of section.children) {
if (path === child.route || path.startsWith(child.route + '/')) {
return child.key
}
}
}
// Placeholder section (no children)
if (section.placeholder && (path === section.route || path.startsWith(section.route + '/'))) {
return section.key
}
}
// Default: first section's first child
return ADMIN_SECTIONS[0]?.children?.[0]?.key || 'global-common'
})
const currentPageTitle = computed(() => route.meta?.title || 'Jio')
function handleMenuClick(key) {
if (keyToPath[key]) {
router.push(keyToPath[key])
if (isMobile.value) {
drawerActive.value = false
}
}
}
</script>
<style scoped>
.jio-logo {
display: flex;
align-items: center;
justify-content: flex-start;
height: 64px;
gap: 11px;
padding: 0 18px;
border-bottom: 1px solid var(--jio-color-border);
}
.jio-logo--drawer {
height: 64px;
}
.logo-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
flex: 0 0 28px;
border-radius: 7px;
background: var(--jio-color-primary);
color: #fff;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.08em;
}
.jio-logo__copy {
display: flex;
align-items: baseline;
gap: 7px;
min-width: 0;
}
.logo-text {
font-size: 17px;
font-weight: 700;
color: var(--jio-color-text);
letter-spacing: .08em;
}
.jio-logo__version {
color: var(--jio-color-muted);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 10px;
letter-spacing: .02em;
}
.jio-admin-content__inner {
width: 100%;
max-width: 1236px;
margin: 0 auto;
padding: 26px 28px 42px;
}
.jio-admin-mobile {
background: var(--jio-color-canvas);
min-height: 100vh;
}
.jio-admin-mobile__body {
padding: 14px 12px 32px;
}
.jio-page-wrapper {
min-height: calc(100vh - 120px);
}
.jio-error-fallback {
padding: 48px 20px;
text-align: center;
}
.jio-error-title {
margin: 0 0 10px;
font-size: 16px;
color: #d03050;
}
.jio-error-msg {
margin: 0 0 16px;
font-size: 13px;
color: #777;
}
@media (max-width: 1100px) {
.jio-admin-content__inner {
padding: 22px 20px 38px;
}
}
</style>