diff --git a/software/h5-uniapp/.agents/skills/pinia/GENERATION.md b/software/h5-uniapp/.agents/skills/pinia/GENERATION.md deleted file mode 100644 index a2c0f71..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/GENERATION.md +++ /dev/null @@ -1,5 +0,0 @@ -# Generation Info - -- **Source:** `sources/pinia` -- **Git SHA:** `55dbfc5c20d4461748996aa74d8c0913e89fb98e` -- **Generated:** 2026-01-28 diff --git a/software/h5-uniapp/.agents/skills/pinia/LICENSE.md b/software/h5-uniapp/.agents/skills/pinia/LICENSE.md deleted file mode 100644 index 29f64e0..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025-PRESENT Anthony Fu - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/software/h5-uniapp/.agents/skills/pinia/SKILL.md b/software/h5-uniapp/.agents/skills/pinia/SKILL.md deleted file mode 100644 index 89cfa7d..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/SKILL.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -name: pinia -description: Pinia official Vue state management library, type-safe and extensible. Use when defining stores, working with state/getters/actions, or implementing store patterns in Vue apps. -metadata: - author: Anthony Fu - version: "2026.1.28" - source: Generated from https://github.com/vuejs/pinia, scripts located at https://github.com/antfu/skills ---- - -# Pinia - -Pinia is the official state management library for Vue, designed to be intuitive and type-safe. It supports both Options API and Composition API styles, with first-class TypeScript support and devtools integration. - -> The skill is based on Pinia v3.0.4, generated at 2026-01-28. - -## Core References - -| Topic | Description | Reference | -|-------|-------------|-----------| -| Stores | Defining stores, state, getters, actions, storeToRefs, subscriptions | [core-stores](references/core-stores.md) | - -## Features - -### Extensibility - -| Topic | Description | Reference | -|-------|-------------|-----------| -| Plugins | Extend stores with custom properties, state, and behavior | [features-plugins](references/features-plugins.md) | - -### Composability - -| Topic | Description | Reference | -|-------|-------------|-----------| -| Composables | Using Vue composables within stores (VueUse, etc.) | [features-composables](references/features-composables.md) | -| Composing Stores | Store-to-store communication, avoiding circular dependencies | [features-composing-stores](references/features-composing-stores.md) | - -## Best Practices - -| Topic | Description | Reference | -|-------|-------------|-----------| -| Testing | Unit testing with @pinia/testing, mocking, stubbing | [best-practices-testing](references/best-practices-testing.md) | -| Outside Components | Using stores in navigation guards, plugins, middlewares | [best-practices-outside-component](references/best-practices-outside-component.md) | - -## Advanced - -| Topic | Description | Reference | -|-------|-------------|-----------| -| SSR | Server-side rendering, state hydration | [advanced-ssr](references/advanced-ssr.md) | -| Nuxt | Nuxt integration, auto-imports, SSR best practices | [advanced-nuxt](references/advanced-nuxt.md) | -| HMR | Hot module replacement for development | [advanced-hmr](references/advanced-hmr.md) | - -## Key Recommendations - -- **Prefer Setup Stores** for complex logic, composables, and watchers -- **Use `storeToRefs()`** when destructuring state/getters to preserve reactivity -- **Actions can be destructured directly** - they're bound to the store -- **Call stores inside functions** not at module scope, especially for SSR -- **Add HMR support** to each store for better development experience -- **Use `@pinia/testing`** for component tests with mocked stores diff --git a/software/h5-uniapp/.agents/skills/pinia/SYNC.md b/software/h5-uniapp/.agents/skills/pinia/SYNC.md deleted file mode 100644 index 622ef75..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/SYNC.md +++ /dev/null @@ -1,5 +0,0 @@ -# Sync Info - -- **Source:** `vendor/antfu/skills/pinia` -- **Git SHA:** `4086f59fa12d308fb05592ca8ce1704fbb940aa3` -- **Synced:** 2026-01-30 diff --git a/software/h5-uniapp/.agents/skills/pinia/references/advanced-hmr.md b/software/h5-uniapp/.agents/skills/pinia/references/advanced-hmr.md deleted file mode 100644 index 3eef5c7..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/references/advanced-hmr.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -name: hot-module-replacement -description: Enable HMR to preserve store state during development ---- - -# Hot Module Replacement (HMR) - -Pinia supports HMR to edit stores without page reload, preserving existing state. - -## Setup - -Add this snippet after each store definition: - -```ts -import { defineStore, acceptHMRUpdate } from 'pinia' - -export const useAuth = defineStore('auth', { - // store options... -}) - -if (import.meta.hot) { - import.meta.hot.accept(acceptHMRUpdate(useAuth, import.meta.hot)) -} -``` - -## Setup Store Example - -```ts -import { defineStore, acceptHMRUpdate } from 'pinia' - -export const useCounterStore = defineStore('counter', () => { - const count = ref(0) - const increment = () => count.value++ - return { count, increment } -}) - -if (import.meta.hot) { - import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot)) -} -``` - -## Bundler Support - -- **Vite:** Officially supported via `import.meta.hot` -- **Webpack:** Uses `import.meta.webpackHot` -- Any bundler implementing the `import.meta.hot` spec should work - -## Nuxt - -With `@pinia/nuxt`, `acceptHMRUpdate` is auto-imported but you still need to add the HMR snippet manually. - -## Benefits - -- Edit store logic without losing state -- Add/remove state, actions, and getters on the fly -- Faster development iteration - - diff --git a/software/h5-uniapp/.agents/skills/pinia/references/advanced-nuxt.md b/software/h5-uniapp/.agents/skills/pinia/references/advanced-nuxt.md deleted file mode 100644 index 569da63..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/references/advanced-nuxt.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -name: nuxt-integration -description: Using Pinia with Nuxt - auto-imports, SSR, and best practices ---- - -# Nuxt Integration - -Pinia works seamlessly with Nuxt 3/4, handling SSR, serialization, and XSS protection automatically. - -## Installation - -```bash -npx nuxi@latest module add pinia -``` - -This installs both `@pinia/nuxt` and `pinia`. If `pinia` isn't installed, add it manually. - -> **npm users:** If you get `ERESOLVE unable to resolve dependency tree`, add to `package.json`: -> ```json -> "overrides": { "vue": "latest" } -> ``` - -## Configuration - -```ts -// nuxt.config.ts -export default defineNuxtConfig({ - modules: ['@pinia/nuxt'], -}) -``` - -## Auto Imports - -These are automatically available: -- `usePinia()` - get pinia instance -- `defineStore()` - define stores -- `storeToRefs()` - extract reactive refs -- `acceptHMRUpdate()` - HMR support - -**All stores in `app/stores/` (Nuxt 4) or `stores/` are auto-imported.** - -### Custom Store Directories - -```ts -// nuxt.config.ts -export default defineNuxtConfig({ - modules: ['@pinia/nuxt'], - pinia: { - storesDirs: ['./stores/**', './custom-folder/stores/**'], - }, -}) -``` - -## Fetching Data in Pages - -Use `callOnce()` for SSR-friendly data fetching: - -```vue - -``` - -### Refetch on Navigation - -```vue - -``` - -## Using Stores Outside Components - -In navigation guards, middlewares, or other stores, pass the `pinia` instance: - -```ts -// middleware/auth.ts -export default defineNuxtRouteMiddleware((to) => { - const nuxtApp = useNuxtApp() - const store = useStore(nuxtApp.$pinia) - - if (to.meta.requiresAuth && !store.isLoggedIn) { - return navigateTo('/login') - } -}) -``` - -Most of the time, you don't need this - just use stores in components or other injection-aware contexts. - -## Pinia Plugins with Nuxt - -Create a Nuxt plugin: - -```ts -// plugins/myPiniaPlugin.ts -import { PiniaPluginContext } from 'pinia' - -function MyPiniaPlugin({ store }: PiniaPluginContext) { - store.$subscribe((mutation) => { - console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}`) - }) - return { creationTime: new Date() } -} - -export default defineNuxtPlugin(({ $pinia }) => { - $pinia.use(MyPiniaPlugin) -}) -``` - - diff --git a/software/h5-uniapp/.agents/skills/pinia/references/advanced-ssr.md b/software/h5-uniapp/.agents/skills/pinia/references/advanced-ssr.md deleted file mode 100644 index 2972f3a..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/references/advanced-ssr.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -name: server-side-rendering -description: SSR setup, state hydration, and avoiding cross-request state pollution ---- - -# Server Side Rendering (SSR) - -Pinia works with SSR when stores are called at the top of `setup`, getters, or actions. - -> **Using Nuxt?** See the [Nuxt integration](advanced-nuxt.md) instead. - -## Basic Usage - -```vue - -``` - -## Using Store Outside setup() - -Pass the `pinia` instance explicitly: - -```ts -const pinia = createPinia() -const app = createApp(App) -app.use(router) -app.use(pinia) - -router.beforeEach((to) => { - // ✅ Pass pinia for correct SSR context - const main = useMainStore(pinia) - - if (to.meta.requiresAuth && !main.isLoggedIn) { - return '/login' - } -}) -``` - -## serverPrefetch() - -Access pinia via `this.$pinia`: - -```ts -export default { - serverPrefetch() { - const store = useStore(this.$pinia) - return store.fetchData() - }, -} -``` - -## onServerPrefetch() - -Works normally: - -```vue - -``` - -## State Hydration - -Serialize state on server and hydrate on client. - -### Server Side - -Use [devalue](https://github.com/Rich-Harris/devalue) for XSS-safe serialization: - -```ts -import devalue from 'devalue' -import { createPinia } from 'pinia' - -const pinia = createPinia() -const app = createApp(App) -app.use(router) -app.use(pinia) - -// After rendering, state is available -const serializedState = devalue(pinia.state.value) -// Inject into HTML as global variable -``` - -### Client Side - -Hydrate before any `useStore()` call: - -```ts -const pinia = createPinia() -const app = createApp(App) -app.use(pinia) - -// Hydrate from serialized state (e.g., from window.__pinia) -if (typeof window !== 'undefined') { - pinia.state.value = JSON.parse(window.__pinia) -} -``` - -## SSR Examples - -- [Vitesse template](https://github.com/antfu/vitesse/blob/main/src/modules/pinia.ts) -- [vite-plugin-ssr](https://vite-plugin-ssr.com/pinia) - -## Key Points - -1. Call stores inside functions, not at module scope -2. Pass `pinia` instance when using stores outside components in SSR -3. Hydrate state before calling any `useStore()` -4. Use `devalue` or similar for safe serialization -5. Avoid cross-request state pollution by creating fresh pinia per request - - diff --git a/software/h5-uniapp/.agents/skills/pinia/references/best-practices-outside-component.md b/software/h5-uniapp/.agents/skills/pinia/references/best-practices-outside-component.md deleted file mode 100644 index 126f7a6..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/references/best-practices-outside-component.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -name: using-stores-outside-components -description: Correctly using stores in navigation guards, plugins, and other non-component contexts ---- - -# Using Stores Outside Components - -Stores need the `pinia` instance, which is automatically injected in components. Outside components, you may need to provide it manually. - -## Single Page Applications - -Call stores **after** pinia is installed: - -```ts -import { useUserStore } from '@/stores/user' -import { createPinia } from 'pinia' -import { createApp } from 'vue' -import App from './App.vue' - -// ❌ Fails - pinia not created yet -const userStore = useUserStore() - -const pinia = createPinia() -const app = createApp(App) -app.use(pinia) - -// ✅ Works - pinia is active -const userStore = useUserStore() -``` - -## Navigation Guards - -**Wrong:** Call at module level - -```ts -import { createRouter } from 'vue-router' -const router = createRouter({ /* ... */ }) - -// ❌ May fail depending on import order -const store = useUserStore() - -router.beforeEach((to) => { - if (store.isLoggedIn) { /* ... */ } -}) -``` - -**Correct:** Call inside the guard - -```ts -router.beforeEach((to) => { - // ✅ Called after pinia is installed - const store = useUserStore() - - if (to.meta.requiresAuth && !store.isLoggedIn) { - return '/login' - } -}) -``` - -## SSR Applications - -Always pass the `pinia` instance to `useStore()`: - -```ts -const pinia = createPinia() -const app = createApp(App) -app.use(router) -app.use(pinia) - -router.beforeEach((to) => { - // ✅ Pass pinia instance - const main = useMainStore(pinia) - - if (to.meta.requiresAuth && !main.isLoggedIn) { - return '/login' - } -}) -``` - -## serverPrefetch() - -Access pinia via `this.$pinia`: - -```ts -export default { - serverPrefetch() { - const store = useStore(this.$pinia) - return store.fetchData() - }, -} -``` - -## onServerPrefetch() - -Works normally in ` -``` - -## Key Takeaway - -Defer `useStore()` calls to functions that run after pinia is installed, rather than calling at module scope. - - diff --git a/software/h5-uniapp/.agents/skills/pinia/references/best-practices-testing.md b/software/h5-uniapp/.agents/skills/pinia/references/best-practices-testing.md deleted file mode 100644 index 7227cd4..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/references/best-practices-testing.md +++ /dev/null @@ -1,212 +0,0 @@ ---- -name: testing -description: Unit testing stores and components with @pinia/testing ---- - -# Testing Stores - -## Unit Testing Stores - -Create a fresh pinia instance for each test: - -```ts -import { setActivePinia, createPinia } from 'pinia' -import { useCounterStore } from '../src/stores/counter' - -describe('Counter Store', () => { - beforeEach(() => { - setActivePinia(createPinia()) - }) - - it('increments', () => { - const counter = useCounterStore() - expect(counter.n).toBe(0) - counter.increment() - expect(counter.n).toBe(1) - }) -}) -``` - -### With Plugins - -```ts -import { setActivePinia, createPinia } from 'pinia' -import { createApp } from 'vue' -import { somePlugin } from '../src/stores/plugin' - -const app = createApp({}) - -beforeEach(() => { - const pinia = createPinia().use(somePlugin) - app.use(pinia) - setActivePinia(pinia) -}) -``` - -## Testing Components - -Install `@pinia/testing`: - -```bash -npm i -D @pinia/testing -``` - -Use `createTestingPinia()`: - -```ts -import { mount } from '@vue/test-utils' -import { createTestingPinia } from '@pinia/testing' -import { useSomeStore } from '@/stores/myStore' - -const wrapper = mount(Counter, { - global: { - plugins: [createTestingPinia()], - }, -}) - -const store = useSomeStore() - -// Manipulate state directly -store.name = 'new name' -store.$patch({ name: 'new name' }) - -// Actions are stubbed by default -store.someAction() -expect(store.someAction).toHaveBeenCalledTimes(1) -``` - -## Initial State - -Set initial state for tests: - -```ts -const wrapper = mount(Counter, { - global: { - plugins: [ - createTestingPinia({ - initialState: { - counter: { n: 20 }, // Store name → initial state - }, - }), - ], - }, -}) -``` - -## Action Stubbing - -### Execute Real Actions - -```ts -createTestingPinia({ stubActions: false }) -``` - -### Selective Stubbing - -```ts -// Only stub specific actions -createTestingPinia({ - stubActions: ['increment', 'reset'], -}) - -// Or use a function -createTestingPinia({ - stubActions: (actionName, store) => { - if (actionName.startsWith('set')) return true - return false - }, -}) -``` - -### Mock Action Return Values - -```ts -import type { Mock } from 'vitest' - -// After getting store -store.someAction.mockResolvedValue('mocked value') -``` - -## Mocking Getters - -Getters are writable in tests: - -```ts -const pinia = createTestingPinia() -const counter = useCounterStore(pinia) - -counter.double = 3 // Override computed value - -// Reset to default behavior -counter.double = undefined -counter.double // Now computed normally -``` - -## Custom Spy Function - -If not using Jest/Vitest with globals: - -```ts -import { vi } from 'vitest' - -createTestingPinia({ - createSpy: vi.fn, -}) -``` - -With Sinon: - -```ts -import sinon from 'sinon' - -createTestingPinia({ - createSpy: sinon.spy, -}) -``` - -## Pinia Plugins in Tests - -Pass plugins to `createTestingPinia()`: - -```ts -import { somePlugin } from '../src/stores/plugin' - -createTestingPinia({ - stubActions: false, - plugins: [somePlugin], -}) -``` - -**Don't use** `testingPinia.use(MyPlugin)` - pass plugins in options. - -## Type-Safe Mocked Store - -```ts -import type { Mock } from 'vitest' -import type { Store, StoreDefinition } from 'pinia' - -function mockedStore unknown>( - useStore: TStoreDef -): TStoreDef extends StoreDefinition - ? Store, { - [K in keyof Actions]: Actions[K] extends (...args: any[]) => any - ? Mock - : Actions[K] - }> - : ReturnType { - return useStore() as any -} - -// Usage -const store = mockedStore(useSomeStore) -store.someAction.mockResolvedValue('value') // Typed! -``` - -## E2E Tests - -No special handling needed - Pinia works normally. - - diff --git a/software/h5-uniapp/.agents/skills/pinia/references/core-stores.md b/software/h5-uniapp/.agents/skills/pinia/references/core-stores.md deleted file mode 100644 index ea6a72b..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/references/core-stores.md +++ /dev/null @@ -1,389 +0,0 @@ ---- -name: stores -description: Defining stores, state, getters, and actions in Pinia ---- - -# Pinia Stores - -Stores are defined using `defineStore()` with a unique name. Each store has three core concepts: **state**, **getters**, and **actions**. - -## Defining Stores - -### Option Stores - -Similar to Vue's Options API: - -```ts -import { defineStore } from 'pinia' - -export const useCounterStore = defineStore('counter', { - state: () => ({ - count: 0, - name: 'Eduardo', - }), - getters: { - doubleCount: (state) => state.count * 2, - }, - actions: { - increment() { - this.count++ - }, - }, -}) -``` - -Think of `state` as `data`, `getters` as `computed`, and `actions` as `methods`. - -### Setup Stores (Recommended) - -Uses Composition API syntax - more flexible and powerful: - -```ts -import { ref, computed } from 'vue' -import { defineStore } from 'pinia' - -export const useCounterStore = defineStore('counter', () => { - const count = ref(0) - const name = ref('Eduardo') - const doubleCount = computed(() => count.value * 2) - - function increment() { - count.value++ - } - - return { count, name, doubleCount, increment } -}) -``` - -In Setup Stores: `ref()` → state, `computed()` → getters, `function()` → actions. - -**Important:** You must return all state properties for Pinia to track them. - -### Using Stores - -```vue - -``` - -### Destructuring with storeToRefs - -```vue - -``` - ---- - -## State - -State is defined as a function returning the initial state. - -### TypeScript - -Type inference works automatically. For complex types: - -```ts -interface UserInfo { - name: string - age: number -} - -export const useUserStore = defineStore('user', { - state: () => ({ - userList: [] as UserInfo[], - user: null as UserInfo | null, - }), -}) -``` - -Or use an interface for the return type: - -```ts -interface State { - userList: UserInfo[] - user: UserInfo | null -} - -export const useUserStore = defineStore('user', { - state: (): State => ({ - userList: [], - user: null, - }), -}) -``` - -### Accessing and Modifying - -```ts -const store = useStore() -store.count++ -``` - -```vue - -``` - -### Mutating with $patch - -Apply multiple changes at once: - -```ts -// Object syntax -store.$patch({ - count: store.count + 1, - name: 'DIO', -}) - -// Function syntax (for complex mutations) -store.$patch((state) => { - state.items.push({ name: 'shoes', quantity: 1 }) - state.hasChanged = true -}) -``` - -### Resetting State - -Option Stores have built-in `$reset()`. For Setup Stores, implement your own: - -```ts -export const useCounterStore = defineStore('counter', () => { - const count = ref(0) - - function $reset() { - count.value = 0 - } - - return { count, $reset } -}) -``` - -### Subscribing to State Changes - -```ts -cartStore.$subscribe((mutation, state) => { - mutation.type // 'direct' | 'patch object' | 'patch function' - mutation.storeId // 'cart' - mutation.payload // patch object (only for 'patch object') - - localStorage.setItem('cart', JSON.stringify(state)) -}) - -// Options -cartStore.$subscribe(callback, { flush: 'sync' }) // Immediate -cartStore.$subscribe(callback, { detached: true }) // Keep after unmount -``` - ---- - -## Getters - -Getters are computed values, equivalent to Vue's `computed()`. - -### Basic Getters - -```ts -getters: { - doubleCount: (state) => state.count * 2, -} -``` - -### Accessing Other Getters - -Use `this` with explicit return type: - -```ts -getters: { - doubleCount: (state) => state.count * 2, - doublePlusOne(): number { - return this.doubleCount + 1 - }, -}, -``` - -### Getters with Arguments - -Return a function (note: loses caching): - -```ts -getters: { - getUserById: (state) => { - return (userId: string) => state.users.find((user) => user.id === userId) - }, -}, -``` - -Cache within parameterized getters: - -```ts -getters: { - getActiveUserById(state) { - const activeUsers = state.users.filter((user) => user.active) - return (userId: string) => activeUsers.find((user) => user.id === userId) - }, -}, -``` - -### Accessing Other Stores in Getters - -```ts -import { useOtherStore } from './other-store' - -getters: { - combined(state) { - const otherStore = useOtherStore() - return state.localData + otherStore.data - }, -}, -``` - ---- - -## Actions - -Actions are methods for business logic. Unlike getters, they can be asynchronous. - -### Defining Actions - -```ts -actions: { - increment() { - this.count++ - }, - randomizeCounter() { - this.count = Math.round(100 * Math.random()) - }, -}, -``` - -### Async Actions - -```ts -actions: { - async registerUser(login: string, password: string) { - try { - this.userData = await api.post({ login, password }) - } catch (error) { - return error - } - }, -}, -``` - -### Accessing Other Stores in Actions - -```ts -import { useAuthStore } from './auth-store' - -actions: { - async fetchUserPreferences() { - const auth = useAuthStore() - if (auth.isAuthenticated) { - this.preferences = await fetchPreferences() - } - }, -}, -``` - -**SSR:** Call all `useStore()` before any `await`: - -```ts -async orderCart() { - // ✅ Call stores before await - const user = useUserStore() - - await apiOrderCart(user.token, this.items) - // ❌ Don't call useStore() after await in SSR -} -``` - -### Subscribing to Actions - -```ts -const unsubscribe = someStore.$onAction( - ({ name, store, args, after, onError }) => { - const startTime = Date.now() - console.log(`Start "${name}" with params [${args.join(', ')}]`) - - after((result) => { - console.log(`Finished "${name}" after ${Date.now() - startTime}ms`) - }) - - onError((error) => { - console.warn(`Failed "${name}": ${error}`) - }) - } -) - -unsubscribe() // Cleanup -``` - -Keep subscription after component unmount: - -```ts -someStore.$onAction(callback, true) -``` - ---- - -## Options API Helpers - -```ts -import { mapState, mapWritableState, mapActions } from 'pinia' -import { useCounterStore } from '../stores/counter' - -export default { - computed: { - // Readonly state/getters - ...mapState(useCounterStore, ['count', 'doubleCount']), - // Writable state - ...mapWritableState(useCounterStore, ['count']), - }, - methods: { - ...mapActions(useCounterStore, ['increment']), - }, -} -``` - ---- - -## Accessing Global Providers in Setup Stores - -```ts -import { inject } from 'vue' -import { useRoute } from 'vue-router' -import { defineStore } from 'pinia' - -export const useSearchFilters = defineStore('search-filters', () => { - const route = useRoute() - const appProvided = inject('appProvided') - - // Don't return these - access them directly in components - return { /* ... */ } -}) -``` - - diff --git a/software/h5-uniapp/.agents/skills/pinia/references/features-composables.md b/software/h5-uniapp/.agents/skills/pinia/references/features-composables.md deleted file mode 100644 index 79f7d94..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/references/features-composables.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -name: composables-in-stores -description: Using Vue composables within Pinia stores ---- - -# Composables in Stores - -Pinia stores can leverage Vue composables for reusable stateful logic. - -## Option Stores - -Call composables inside the `state` property, but only those returning writable refs: - -```ts -import { defineStore } from 'pinia' -import { useLocalStorage } from '@vueuse/core' - -export const useAuthStore = defineStore('auth', { - state: () => ({ - user: useLocalStorage('pinia/auth/login', 'bob'), - }), -}) -``` - -**Works:** Composables returning `ref()`: -- `useLocalStorage` -- `useAsyncState` - -**Doesn't work in Option Stores:** -- Composables exposing functions -- Composables exposing readonly data - -## Setup Stores - -More flexible - can use almost any composable: - -```ts -import { defineStore } from 'pinia' -import { useMediaControls } from '@vueuse/core' -import { ref } from 'vue' - -export const useVideoPlayer = defineStore('video', () => { - const videoElement = ref() - const src = ref('/data/video.mp4') - const { playing, volume, currentTime, togglePictureInPicture } = - useMediaControls(videoElement, { src }) - - function loadVideo(element: HTMLVideoElement, newSrc: string) { - videoElement.value = element - src.value = newSrc - } - - return { - src, - playing, - volume, - currentTime, - loadVideo, - togglePictureInPicture, - } -}) -``` - -**Note:** Don't return non-serializable DOM refs like `videoElement` - they're internal implementation details. - -## SSR Considerations - -### Option Stores with hydrate() - -Define a `hydrate()` function to handle client-side hydration: - -```ts -import { defineStore } from 'pinia' -import { useLocalStorage } from '@vueuse/core' - -export const useAuthStore = defineStore('auth', { - state: () => ({ - user: useLocalStorage('pinia/auth/login', 'bob'), - }), - - hydrate(state, initialState) { - // Ignore server state, read from browser - state.user = useLocalStorage('pinia/auth/login', 'bob') - }, -}) -``` - -### Setup Stores with skipHydrate() - -Mark state that shouldn't hydrate from server: - -```ts -import { defineStore, skipHydrate } from 'pinia' -import { useEyeDropper, useLocalStorage } from '@vueuse/core' - -export const useColorStore = defineStore('colors', () => { - const { isSupported, open, sRGBHex } = useEyeDropper() - const lastColor = useLocalStorage('lastColor', sRGBHex) - - return { - // Skip hydration for client-only state - lastColor: skipHydrate(lastColor), - open, // Function - no hydration needed - isSupported, // Boolean - not reactive - } -}) -``` - -`skipHydrate()` only applies to state properties (refs), not functions or non-reactive values. - - diff --git a/software/h5-uniapp/.agents/skills/pinia/references/features-composing-stores.md b/software/h5-uniapp/.agents/skills/pinia/references/features-composing-stores.md deleted file mode 100644 index 948f8b5..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/references/features-composing-stores.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -name: composing-stores -description: Store-to-store communication and avoiding circular dependencies ---- - -# Composing Stores - -Stores can use each other for shared state and logic. - -## Rule: Avoid Circular Dependencies - -Two stores cannot directly read each other's state during setup: - -```ts -// ❌ Infinite loop -const useX = defineStore('x', () => { - const y = useY() - y.name // Don't read here! - return { name: ref('X') } -}) - -const useY = defineStore('y', () => { - const x = useX() - x.name // Don't read here! - return { name: ref('Y') } -}) -``` - -**Solution:** Read in getters, computed, or actions: - -```ts -const useX = defineStore('x', () => { - const y = useY() - - // ✅ Read in computed/actions - function doSomething() { - const yName = y.name - } - - return { name: ref('X'), doSomething } -}) -``` - -## Setup Stores: Use Store at Top - -```ts -import { defineStore } from 'pinia' -import { useUserStore } from './user' - -export const useCartStore = defineStore('cart', () => { - const user = useUserStore() - const list = ref([]) - - const summary = computed(() => { - return `Hi ${user.name}, you have ${list.value.length} items` - }) - - function purchase() { - return apiPurchase(user.id, list.value) - } - - return { list, summary, purchase } -}) -``` - -## Shared Getters - -Call `useStore()` inside a getter: - -```ts -import { useUserStore } from './user' - -export const useCartStore = defineStore('cart', { - getters: { - summary(state) { - const user = useUserStore() - return `Hi ${user.name}, you have ${state.list.length} items` - }, - }, -}) -``` - -## Shared Actions - -Call `useStore()` inside an action: - -```ts -import { useUserStore } from './user' -import { apiOrderCart } from './api' - -export const useCartStore = defineStore('cart', { - actions: { - async orderCart() { - const user = useUserStore() - - try { - await apiOrderCart(user.token, this.items) - this.emptyCart() - } catch (err) { - displayError(err) - } - }, - }, -}) -``` - -## SSR: Call Stores Before Await - -In async actions, call all stores before any `await`: - -```ts -actions: { - async orderCart() { - // ✅ All useStore() calls before await - const user = useUserStore() - const analytics = useAnalyticsStore() - - try { - await apiOrderCart(user.token, this.items) - // ❌ Don't call useStore() after await (SSR issue) - // const otherStore = useOtherStore() - } catch (err) { - displayError(err) - } - }, -} -``` - -This ensures the correct Pinia instance is used during SSR. - - diff --git a/software/h5-uniapp/.agents/skills/pinia/references/features-plugins.md b/software/h5-uniapp/.agents/skills/pinia/references/features-plugins.md deleted file mode 100644 index c4355ac..0000000 --- a/software/h5-uniapp/.agents/skills/pinia/references/features-plugins.md +++ /dev/null @@ -1,203 +0,0 @@ ---- -name: plugins -description: Extend stores with custom properties, methods, and behavior ---- - -# Plugins - -Plugins extend all stores with custom properties, methods, or behavior. - -## Basic Plugin - -```ts -import { createPinia } from 'pinia' - -function SecretPiniaPlugin() { - return { secret: 'the cake is a lie' } -} - -const pinia = createPinia() -pinia.use(SecretPiniaPlugin) - -// In any store -const store = useStore() -store.secret // 'the cake is a lie' -``` - -## Plugin Context - -Plugins receive a context object: - -```ts -import { PiniaPluginContext } from 'pinia' - -export function myPiniaPlugin(context: PiniaPluginContext) { - context.pinia // pinia instance - context.app // Vue app instance - context.store // store being augmented - context.options // store definition options -} -``` - -## Adding Properties - -Return an object to add properties (tracked in devtools): - -```ts -pinia.use(() => ({ hello: 'world' })) -``` - -Or set directly on store: - -```ts -pinia.use(({ store }) => { - store.hello = 'world' - // For devtools visibility in dev mode - if (process.env.NODE_ENV === 'development') { - store._customProperties.add('hello') - } -}) -``` - -## Adding State - -Add to both `store` and `store.$state` for SSR/devtools: - -```ts -import { toRef, ref } from 'vue' - -pinia.use(({ store }) => { - if (!store.$state.hasOwnProperty('hasError')) { - const hasError = ref(false) - store.$state.hasError = hasError - } - store.hasError = toRef(store.$state, 'hasError') -}) -``` - -## Adding External Properties - -Wrap non-reactive objects with `markRaw()`: - -```ts -import { markRaw } from 'vue' -import { router } from './router' - -pinia.use(({ store }) => { - store.router = markRaw(router) -}) -``` - -## Custom Store Options - -Define custom options consumed by plugins: - -```ts -// Store definition -defineStore('search', { - actions: { - searchContacts() { /* ... */ }, - }, - debounce: { - searchContacts: 300, - }, -}) - -// Plugin reads custom option -import debounce from 'lodash/debounce' - -pinia.use(({ options, store }) => { - if (options.debounce) { - return Object.keys(options.debounce).reduce((acc, action) => { - acc[action] = debounce(store[action], options.debounce[action]) - return acc - }, {}) - } -}) -``` - -For Setup Stores, pass options as third argument: - -```ts -defineStore( - 'search', - () => { /* ... */ }, - { - debounce: { searchContacts: 300 }, - } -) -``` - -## TypeScript Augmentation - -### Custom Properties - -```ts -import 'pinia' -import type { Router } from 'vue-router' - -declare module 'pinia' { - export interface PiniaCustomProperties { - router: Router - hello: string - } -} -``` - -### Custom State - -```ts -declare module 'pinia' { - export interface PiniaCustomStateProperties { - hasError: boolean - } -} -``` - -### Custom Options - -```ts -declare module 'pinia' { - export interface DefineStoreOptionsBase { - debounce?: Partial, number>> - } -} -``` - -## Subscribe in Plugins - -```ts -pinia.use(({ store }) => { - store.$subscribe(() => { - // React to state changes - }) - store.$onAction(() => { - // React to actions - }) -}) -``` - -## Nuxt Plugin - -Create a Nuxt plugin to add Pinia plugins: - -```ts -// plugins/myPiniaPlugin.ts -import { PiniaPluginContext } from 'pinia' - -function MyPiniaPlugin({ store }: PiniaPluginContext) { - store.$subscribe((mutation) => { - console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}`) - }) - return { creationTime: new Date() } -} - -export default defineNuxtPlugin(({ $pinia }) => { - $pinia.use(MyPiniaPlugin) -}) -``` - - diff --git a/software/h5-uniapp/.agents/skills/uni-app/GENERATION.md b/software/h5-uniapp/.agents/skills/uni-app/GENERATION.md deleted file mode 100644 index be7a721..0000000 --- a/software/h5-uniapp/.agents/skills/uni-app/GENERATION.md +++ /dev/null @@ -1,54 +0,0 @@ -# Generation Info - -- **Source:** `sources/uniapp` -- **Git SHA:** `91423a240a5b0714fc5e7e13bd3f93a79b8cc8d7` -- **Generated:** 2026-01-30 - -## Generated Skills - -| Category | File | Description | -|----------|------|-------------| -| Core | core-framework.md | Framework basics, platform support, condition compilation | -| Core | core-view-components.md | Container components: view, scroll-view, swiper, movable-area | -| Core | core-form-components.md | Form controls: input, textarea, picker, checkbox, radio | -| Features | feature-media-components.md | Media: image, video, camera, map, live-player | -| Features | feature-navigation.md | Navigation: navigator, routing APIs | -| Features | feature-network.md | Network: request, upload, download, WebSocket | -| Features | feature-storage.md | Storage: localStorage, file system | -| Features | feature-system-info.md | System: device info, network, screen, vibration | -| Features | feature-file-operations.md | Files: image/video selection, file operations | -| Features | feature-location.md | Location: geolocation, map component | -| Features | feature-ui-feedback.md | UI: toast, modal, loading, action sheet | -| Features | feature-lifecycle.md | Lifecycle: app and page hooks | -| Config | config-pages.md | pages.json configuration | -| Config | config-manifest.md | manifest.json configuration | - -## Source Documentation Structure - -``` -sources/uniapp/docs/ -├── README.md # Framework overview -├── api/ # API documentation -│ ├── README.md # API overview -│ ├── request/ # Network APIs -│ ├── storage/ # Storage APIs -│ ├── media/ # Media APIs -│ ├── file/ # File APIs -│ ├── location/ # Location APIs -│ ├── ui/ # UI APIs -│ └── system/ # System APIs -├── component/ # Component documentation -├── collocation/ # Configuration -│ ├── pages.md # pages.json -│ ├── manifest.md # manifest.json -│ └── manifest-app.md # App-specific manifest -├── tutorial/ # Tutorials -└── quickstart.md # Getting started -``` - -## Notes - -- Skills focus on practical usage patterns and code examples -- Platform-specific differences are highlighted where relevant -- Configuration files include common use cases for each platform -- API skills include both callback and Promise usage patterns diff --git a/software/h5-uniapp/.agents/skills/uni-app/SKILL.md b/software/h5-uniapp/.agents/skills/uni-app/SKILL.md deleted file mode 100644 index f2bae87..0000000 --- a/software/h5-uniapp/.agents/skills/uni-app/SKILL.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -name: uni-app -description: Comprehensive skill reference for uni-app cross-platform development framework -metadata: - author: FlippeDround - version: "2026.1.30" - source: Generated from https://gitcode.com/dcloud/unidocs-zh, skills located at https://github.com/antfu/skills ---- - -> The skill is based on uni-app documentation, generated at 2026-01-30. - -uni-app is a Vue.js-based cross-platform framework for developing applications that run on iOS, Android, HarmonyOS, Web (responsive), and various mini-program platforms (WeChat/Alipay/Baidu/Douyin/Feishu/QQ/Kuaishou/DingTalk/Taobao/Jingdong/Xiaohongshu). - -## Core - -| Topic | Description | Reference | -|-------|-------------|-----------| -| Core Framework | Project structure, platform support, condition compilation | [core-framework](references/core-framework.md) | -| View Components | view, scroll-view, swiper, movable-area, cover-view | [core-view-components](references/core-view-components.md) | -| Form Components | input, textarea, picker, checkbox, radio, switch, slider | [core-form-components](references/core-form-components.md) | - -## Features - -### UI Components - -| Topic | Description | Reference | -|-------|-------------|-----------| -| Media Components | image, video, camera, live-player, map | [feature-media-components](references/feature-media-components.md) | -| Navigation | navigator, routing, page navigation | [feature-navigation](references/feature-navigation.md) | -| UI Feedback | toast, modal, loading, action sheet, pull refresh | [feature-ui-feedback](references/feature-ui-feedback.md) | - -### APIs - -| Topic | Description | Reference | -|-------|-------------|-----------| -| Network | HTTP requests, file upload/download, WebSocket | [feature-network](references/feature-network.md) | -| Storage | Local storage, file system, caching | [feature-storage](references/feature-storage.md) | -| System Info | Device info, network status, screen, vibration | [feature-system-info](references/feature-system-info.md) | -| File Operations | Image/video selection, file system operations | [feature-file-operations](references/feature-file-operations.md) | -| Location | Geolocation, map component, address selection | [feature-location](references/feature-location.md) | -| Lifecycle | App and page lifecycle hooks | [feature-lifecycle](references/feature-lifecycle.md) | - -## Configuration - -| Topic | Description | Reference | -|-------|-------------|-----------| -| pages.json | Page routing, tab bar, global styles, sub-packages | [config-pages](references/config-pages.md) | -| manifest.json | App config, permissions, platform settings | [config-manifest](references/config-manifest.md) | - -## Platform Support - -| Platform | Support Level | -|----------|---------------| -| iOS App | Full support | -| Android App | Full support | -| HarmonyOS Next | Full support | -| H5/Web | Full support | -| WeChat Mini Program | Full support | -| Alipay Mini Program | Full support | -| Baidu Smart Program | Full support | -| Douyin Mini Program | Full support | -| QQ Mini Program | Full support | -| Kuaishou Mini Program | Full support | -| Feishu Mini Program | Full support | -| JD Mini Program | Full support | -| HarmonyOS Meta Service | Full support | - -## Key Concepts - -### Condition Compilation - -Use special comments to write platform-specific code: - -```vue - -App only - - - -WeChat only - -``` - -### API Promise Support - -Most uni-app APIs support Promise when no callback is provided: - -```javascript -const res = await uni.request({ url: 'https://api.example.com' }) -``` - -### Cross-Platform Best Practices - -1. Use uni-app components and APIs instead of platform-specific ones -2. Use condition compilation for platform-specific features -3. Test on all target platforms -4. Use rpx for responsive layouts -5. Handle platform differences in manifest.json -6. must use uni-helper tools - - -### MCP扩展 - -当需要查询 uni-app 官方文档时,优先调用 `search-docs-by-Uniapp-official` MCP 工具搜索相关 API 文档和使用示例。 - -**使用场景:** -- 用户询问特定 API 的详细用法 -- 需要官方文档中的代码示例 -- 查询组件的属性和事件 -- 了解 API 的平台兼容性 - -**工具安装:** -如果检测到该 MCP 工具不可用,引导用户访问 https://github.com/uni-helper/mcp 进行安装。 diff --git a/software/h5-uniapp/.agents/skills/uni-app/references/config-manifest.md b/software/h5-uniapp/.agents/skills/uni-app/references/config-manifest.md deleted file mode 100644 index 9a8c0cb..0000000 --- a/software/h5-uniapp/.agents/skills/uni-app/references/config-manifest.md +++ /dev/null @@ -1,370 +0,0 @@ ---- -name: Manifest.json Configuration -description: App configuration, permissions, and platform-specific settings ---- - -# Manifest.json Configuration - -`manifest.json` is the application configuration file, defining app name, icon, permissions, and platform-specific settings. - -## Basic Configuration - -```json -{ - "name": "My App", - "appid": "__UNI__XXXXXXX", - "description": "App description", - "versionName": "1.0.0", - "versionCode": 100, - "locale": "auto", - "debug": false -} -``` - -### Basic Properties - -| Property | Type | Description | -|----------|------|-------------| -| name | String | App name | -| appid | String | DCloud app ID | -| description | String | App description | -| versionName | String | Version name (1.0.0) | -| versionCode | Number | Version code (integer) | -| locale | String | Default language | -| debug | Boolean | Debug mode | -| networkTimeout | Object | Network timeout settings | -| uniStatistics | Object | Statistics configuration | - -## Network Timeout - -```json -{ - "networkTimeout": { - "request": 60000, - "connectSocket": 60000, - "uploadFile": 60000, - "downloadFile": 60000 - } -} -``` - -## App Configuration (app-plus) - -```json -{ - "app-plus": { - "splashscreen": { - "alwaysShowBeforeRender": true, - "autoclose": true, - "waiting": true - }, - "screenOrientation": ["portrait-primary"], - "modules": { - "OAuth": {}, - "Payment": {}, - "Push": {} - }, - "distribute": { - "android": { - "permissions": [ - "" - ], - "minSdkVersion": 21, - "targetSdkVersion": 30 - }, - "ios": { - "capabilities": { - "entitlements": { - "com.apple.developer.push": true - } - } - }, - "sdkConfigs": { - "payment": { - "alipay": {}, - "weixin": {} - } - } - }, - "optimization": { - "subPackages": true - } - } -} -``` - -### Splash Screen - -| Property | Type | Description | -|----------|------|-------------| -| alwaysShowBeforeRender | Boolean | Show until first page renders | -| autoclose | Boolean | Auto close splash screen | -| waiting | Boolean | Show loading indicator | - -## Mini-Program Configuration - -### WeChat (mp-weixin) - -```json -{ - "mp-weixin": { - "appid": "wx1234567890", - "setting": { - "urlCheck": false, - "es6": true, - "postcss": true, - "minified": true - }, - "usingComponents": true, - "permission": { - "scope.userLocation": { - "desc": "Your location is needed" - } - }, - "requiredPrivateInfos": [ - "getLocation" - ] - } -} -``` - -### Alipay (mp-alipay) - -```json -{ - "mp-alipay": { - "appid": "2021...", - "allowsAlignRight": true, - "component2": true - } -} -``` - -### Baidu (mp-baidu) - -```json -{ - "mp-baidu": { - "appid": "12345678", - "navigationBarForceEnable": true - } -} -``` - -### Douyin (mp-toutiao) - -```json -{ - "mp-toutiao": { - "appid": "tt...", - "setting": { - "es6": true, - "minified": true - } - } -} -``` - -## H5 Configuration - -```json -{ - "h5": { - "title": "My App", - "template": "index.html", - "router": { - "mode": "hash", - "base": "./" - }, - "optimization": { - "treeShaking": { - "enable": true - } - }, - "publicPath": "./", - "devServer": { - "port": 8080, - "disableHostCheck": true - }, - "sdkConfigs": { - "maps": { - "qqmap": { - "key": "..." - } - } - } - } -} -``` - -## Vue Configuration - -### Vue 2 - -```json -{ - "vueVersion": "2", - "sassImplementationName": "dart-sass" -} -``` - -### Vue 3 - -```json -{ - "vueVersion": "3" -} -``` - -## Permission Configuration - -### Android Permissions - -```json -{ - "app-plus": { - "distribute": { - "android": { - "permissions": [ - "", - "", - "", - "", - "" - ] - } - } - } -} -``` - -### iOS Privacy Descriptions - -```json -{ - "app-plus": { - "distribute": { - "ios": { - "privacyDescription": { - "NSCameraUsageDescription": "Camera access is needed for scanning QR codes", - "NSPhotoLibraryUsageDescription": "Photo access is needed for uploading images", - "NSLocationWhenInUseUsageDescription": "Location is needed for finding nearby stores" - } - } - } - } -} -``` - -## Common Module Configuration - -### OAuth (Login) - -```json -{ - "app-plus": { - "modules": { - "OAuth": {} - }, - "distribute": { - "sdkConfigs": { - "oauth": { - "weixin": { - "appid": "wx...", - "appsecret": "...", - "UniversalLinks": "https://..." - } - } - } - } - } -} -``` - -### Payment - -```json -{ - "app-plus": { - "modules": { - "Payment": {} - }, - "distribute": { - "sdkConfigs": { - "payment": { - "alipay": {}, - "weixin": { - "appid": "wx..." - } - } - } - } - } -} -``` - -### Push Notifications - -```json -{ - "app-plus": { - "modules": { - "Push": {} - }, - "distribute": { - "sdkConfigs": { - "push": { - "unipush": {} - } - } - } - } -} -``` - -### Share - -```json -{ - "app-plus": { - "modules": { - "Share": {} - }, - "distribute": { - "sdkConfigs": { - "share": { - "weixin": { - "appid": "wx..." - } - } - } - } - } -} -``` - -## Statistics Configuration - -```json -{ - "uniStatistics": { - "enable": true - }, - "app-plus": { - "uniStatistics": { - "enable": true - } - }, - "mp-weixin": { - "uniStatistics": { - "enable": true - } - } -} -``` - - diff --git a/software/h5-uniapp/.agents/skills/uni-app/references/config-pages.md b/software/h5-uniapp/.agents/skills/uni-app/references/config-pages.md deleted file mode 100644 index a117feb..0000000 --- a/software/h5-uniapp/.agents/skills/uni-app/references/config-pages.md +++ /dev/null @@ -1,270 +0,0 @@ ---- -name: Pages.json Configuration -description: Page routing, tab bar, and global style configuration ---- - -# Pages.json Configuration - -`pages.json` is the global configuration file for uni-app, defining page routes, window styles, native navigation bar, and tab bar. - -## Basic Structure - -```json -{ - "pages": [ - { - "path": "pages/index/index", - "style": { - "navigationBarTitleText": "Home" - } - } - ], - "globalStyle": { - "navigationBarTextStyle": "black", - "navigationBarBackgroundColor": "#F8F8F8" - }, - "tabBar": { - "list": [ - { "pagePath": "pages/index/index", "text": "Home" } - ] - } -} -``` - -## Root Properties - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| pages | Array | Yes | Page routes | -| globalStyle | Object | No | Default window style | -| tabBar | Object | No | Tab bar configuration | -| condition | Object | No | Launch mode (dev only) | -| subPackages | Array | No | Sub-packages | -| preloadRule | Object | No | Preload rules (MP) | -| easycom | Object | No | Auto component import | -| leftWindow/topWindow/rightWindow | Object | No | Multi-window (H5) | - -## Pages Configuration - -```json -{ - "pages": [ - { - "path": "pages/index/index", - "style": { - "navigationBarTitleText": "Home", - "enablePullDownRefresh": true - } - }, - { - "path": "pages/detail/detail", - "style": { - "navigationBarTitleText": "Detail", - "navigationStyle": "custom" - } - } - ] -} -``` - -### Page Style Options - -| Property | Type | Description | -|----------|------|-------------| -| navigationBarTitleText | String | Navigation title | -| navigationBarTextStyle | String | Title color: black/white | -| navigationBarBackgroundColor | HexColor | Nav background | -| navigationStyle | String | default/custom | -| enablePullDownRefresh | Boolean | Enable pull refresh | -| backgroundColor | HexColor | Background color | -| backgroundTextStyle | String | dark/light | -| onReachBottomDistance | Number | Bottom distance (px) | -| disableScroll | Boolean | Disable scroll (MP) | -| usingComponents | Object | Using custom components | - -## Global Style - -Applied to all pages unless overridden. - -```json -{ - "globalStyle": { - "navigationBarTextStyle": "black", - "navigationBarTitleText": "My App", - "navigationBarBackgroundColor": "#F8F8F8", - "backgroundColor": "#F8F8F8", - "backgroundTextStyle": "dark", - "enablePullDownRefresh": false, - "onReachBottomDistance": 50, - "rpxCalcMaxDeviceWidth": 960, - "rpxCalcBaseDeviceWidth": 375, - "rpxCalcIncludeWidth": 750 - } -} -``` - -## Tab Bar Configuration - -```json -{ - "tabBar": { - "color": "#7A7E83", - "selectedColor": "#3cc51f", - "backgroundColor": "#ffffff", - "borderStyle": "black", - "list": [ - { - "pagePath": "pages/index/index", - "text": "Home", - "iconPath": "static/image/home.png", - "selectedIconPath": "static/image/home-active.png" - }, - { - "pagePath": "pages/category/category", - "text": "Category", - "iconPath": "static/image/cat.png", - "selectedIconPath": "static/image/cat-active.png" - }, - { - "pagePath": "pages/cart/cart", - "text": "Cart", - "iconPath": "static/image/cart.png", - "selectedIconPath": "static/image/cart-active.png" - }, - { - "pagePath": "pages/user/user", - "text": "User", - "iconPath": "static/image/user.png", - "selectedIconPath": "static/image/user-active.png" - } - ] - } -} -``` - -### Tab Bar Properties - -| Property | Type | Description | -|----------|------|-------------| -| color | HexColor | Unselected text color | -| selectedColor | HexColor | Selected text color | -| backgroundColor | HexColor | Background color | -| borderStyle | String | black/white | -| list | Array | Tab items (2-5 items) | -| position | String | bottom/top | - -### Tab Item Properties - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| pagePath | String | Yes | Page path | -| text | String | Yes | Tab text | -| iconPath | String | No | Icon path (81x81px) | -| selectedIconPath | String | No | Selected icon | - -### Custom Tab Bar (WeChat/QQ/Douyin) - -```json -{ - "tabBar": { - "custom": true, - "list": [ - { "pagePath": "pages/index/index", "text": "Home" } - ] - } -} -``` - -Create `custom-tab-bar/index` component in root. - -## Sub-packages - -Split app into smaller chunks for faster loading. - -```json -{ - "subPackages": [ - { - "root": "packageA", - "pages": [ - { "path": "pages/cat/cat" }, - { "path": "pages/dog/dog" } - ] - }, - { - "root": "packageB", - "pages": [ - { "path": "pages/apple/apple" }, - { "path": "pages/banana/banana" } - ] - } - ], - "preloadRule": { - "pages/index/index": { - "network": "all", - "packages": ["packageA"] - } - } -} -``` - -## EasyCom (Auto Component Import) - -```json -{ - "easycom": { - "autoscan": true, - "custom": { - "^u--(.*)": "uview-plus/components/u-$1/u-$1.vue", - "^up-(.*)": "uview-plus/components/u-$1/u-$1.vue", - "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" - } - } -} -``` - -With this config, components are auto-imported without registration. - -## Conditional Compilation in Config - -```json -{ - "pages": [ - { - "path": "pages/index/index", - "style": { - "navigationBarTitleText": "Home" - } - } - ], - "globalStyle": { - "navigationBarTextStyle": "black" - }, - "condition": { - "current": 0, - "list": [ - { - "name": "test", - "path": "pages/test/test", - "query": "id=1" - } - ] - }, - "mp-weixin": { - "appid": "wx...", - "setting": { - "urlCheck": false - } - }, - "app-plus": { - "splashscreen": { - "alwaysShowBeforeRender": true - } - } -} -``` - - diff --git a/software/h5-uniapp/.agents/skills/uni-app/references/core-form-components.md b/software/h5-uniapp/.agents/skills/uni-app/references/core-form-components.md deleted file mode 100644 index 6c51dca..0000000 --- a/software/h5-uniapp/.agents/skills/uni-app/references/core-form-components.md +++ /dev/null @@ -1,250 +0,0 @@ ---- -name: Form Components -description: Input, selection, and form control components ---- - -# Form Components - -## input - -Single-line text input. - -```vue - -``` - -**Properties:** - -| Property | Type | Default | Description | -|----------|------|---------|-------------| -| value | String | | Input value | -| type | String | text | Input type: text/number/idcard/digit/tel | -| password | Boolean | false | Password input | -| placeholder | String | | Placeholder text | -| placeholder-style | String | | Placeholder inline styles | -| placeholder-class | String | | Placeholder CSS class | -| maxlength | Number | 140 | Max character length | -| cursor-spacing | Number | 0 | Cursor distance from keyboard bottom (px) | -| focus | Boolean | false | Auto focus | -| confirm-type | String | done | Return key type: send/search/next/go/done | -| confirm-hold | Boolean | false | Keep keyboard open after confirm | - -**Events:** -- `@input` - Input value change -- `@focus` - Input focused -- `@blur` - Input blurred -- `@confirm` - Confirm button clicked -- `@keyboardheightchange` - Keyboard height changed - -## textarea - -Multi-line text input. - -```vue - -``` - -**Correct:** -```html - - - -``` - -```html - - -``` - -## Reference -- [Vue.js Form Input Bindings - Multiline text](https://vuejs.org/guide/essentials/forms.html#multiline-text) diff --git a/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-animate-transform-opacity.md b/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-animate-transform-opacity.md deleted file mode 100644 index 9d05d4e..0000000 --- a/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-animate-transform-opacity.md +++ /dev/null @@ -1,186 +0,0 @@ ---- -title: Prefer Animating Transform and Opacity for Performance -impact: MEDIUM -impactDescription: Animating layout-triggering properties like height or margin causes expensive reflows and janky animations -type: best-practice -tags: [vue3, transition, animation, performance, css, transform, opacity] ---- - -# Prefer Animating Transform and Opacity for Performance - -**Impact: MEDIUM** - When creating Vue transitions, you should prefer animating `transform` and `opacity` properties. These are GPU-accelerated and do not trigger expensive CSS layout recalculations. Animating properties like `height`, `width`, `margin`, `padding`, or `top`/`left` causes the browser to recalculate layout on every frame, resulting in janky, low-performance animations. - -## Task Checklist - -- [ ] Use `transform` for position and size animations (translate, scale, rotate) -- [ ] Use `opacity` for fade effects -- [ ] Avoid animating `height`, `width`, `margin`, `padding`, `top`, `left`, `right`, `bottom` -- [ ] If you must animate height, consider using `max-height` with a generous value or JavaScript-based solutions -- [ ] Use `will-change` sparingly and only when needed - -**Problematic Code:** -```css -/* BAD: Animating height triggers layout recalculation every frame */ -.slide-enter-active, -.slide-leave-active { - transition: height 0.3s ease; -} - -.slide-enter-from, -.slide-leave-to { - height: 0; -} - -.slide-enter-to, -.slide-leave-from { - height: 200px; -} -``` - -```css -/* BAD: Animating margin causes layout thrashing */ -.slide-enter-active, -.slide-leave-active { - transition: margin-top 0.3s ease; -} - -.slide-enter-from, -.slide-leave-to { - margin-top: -100%; -} -``` - -**Correct Code:** -```css -/* GOOD: Using transform and opacity - GPU accelerated */ -.fade-enter-active, -.fade-leave-active { - transition: opacity 0.3s ease; -} - -.fade-enter-from, -.fade-leave-to { - opacity: 0; -} -``` - -```css -/* GOOD: Using transform for slide animations */ -.slide-enter-active, -.slide-leave-active { - transition: transform 0.3s ease, opacity 0.3s ease; -} - -.slide-enter-from { - transform: translateX(-100%); - opacity: 0; -} - -.slide-leave-to { - transform: translateX(100%); - opacity: 0; -} -``` - -```css -/* GOOD: Using scale instead of width/height */ -.scale-enter-active, -.scale-leave-active { - transition: transform 0.3s ease, opacity 0.3s ease; -} - -.scale-enter-from, -.scale-leave-to { - transform: scale(0); - opacity: 0; -} -``` - -## When You Must Animate Height - -If you absolutely need to animate height (e.g., accordion), consider these alternatives: - -```vue - - - -``` - -```vue - - - -``` - -## Performance Comparison - -| Property | Layout | Paint | Composite | Performance | -|----------|--------|-------|-----------|-------------| -| `transform` | No | No | Yes | Excellent | -| `opacity` | No | No | Yes | Excellent | -| `background-color` | No | Yes | Yes | Good | -| `width`/`height` | Yes | Yes | Yes | Poor | -| `margin`/`padding` | Yes | Yes | Yes | Poor | -| `top`/`left` | Yes | Yes | Yes | Poor | - -## Reference -- [Vue.js Transition Documentation](https://vuejs.org/guide/built-ins/transition.html) -- [CSS Triggers](https://csstriggers.com/) - Reference for which properties trigger layout/paint -- [High Performance Animations](https://web.dev/animations-guide/) diff --git a/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-flip-inline-elements.md b/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-flip-inline-elements.md deleted file mode 100644 index f2c0e8d..0000000 --- a/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-flip-inline-elements.md +++ /dev/null @@ -1,152 +0,0 @@ ---- -title: TransitionGroup FLIP Animations Do Not Work With Inline Elements -impact: MEDIUM -impactDescription: Move animations silently fail on inline elements, causing items to jump -type: gotcha -tags: [vue3, transition-group, animation, flip, css, display, inline-block] ---- - -# TransitionGroup FLIP Animations Do Not Work With Inline Elements - -**Impact: MEDIUM** - The FLIP (First, Last, Invert, Play) animation technique that Vue uses for `` move transitions does not work with elements that have `display: inline`. The move animation will silently fail, and items will jump to their new positions instead of smoothly transitioning. - -This is a CSS limitation, not a Vue bug. CSS transforms (which FLIP uses internally) do not apply to inline elements per the CSS specification. - -## Task Checklist - -- [ ] Ensure list items are not `display: inline` elements -- [ ] Use `display: inline-block` or `display: block` for list items -- [ ] Use flexbox or grid containers which make children block-level -- [ ] Check if inherited styles are setting `display: inline` - -**Incorrect - Inline elements break move animations:** -```vue - - - -``` - -**Correct - Use inline-block:** -```vue - - - -``` - -**Correct - Use flexbox container:** -```vue - - - -``` - -**Correct - Use block elements:** -```vue - - - -``` - -## Why Inline Elements Don't Work - -Per CSS specifications, the `transform` property does not apply to inline boxes. Since FLIP animations use CSS transforms to animate element positions: - -```css -/* Vue internally applies something like this during move */ -.item { - transform: translateX(-50px) translateY(-20px); - /* Then transitions to transform: none */ -} -``` - -This transform is ignored on inline elements, so no animation occurs. - -## Elements That Are Inline by Default - -Be aware of these common inline elements that need `display: inline-block`: - -- `` -- `` -- ``, ``, ``, `` -- ``, `` -- `