AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Mp Project Init

skill-sirguanzz-claude-skills-mp-project-init · by SirGuanZz

>-

No reviews yet
0 installs
32 views
0.0% view→install

Install

$ agentstack add skill-sirguanzz-claude-skills-mp-project-init

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-sirguanzz-claude-skills-mp-project-init)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Mp Project Init? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

mp-project-init: uni-app 微信小程序从零初始化

uni-app(Vue3 + Vite + TS) 从 0 起微信小程序骨架:脚手架 → 多域名拦截器 → 登录页 → tabBar → 编译验证。

核心纪律只问 5 个问题,答完立刻一口气跑到底——degit 脚手架、npm install、写代码、dev:mp-weixin + build:mp-weixin,全程不暂停。编译产物导入微信开发者工具验证。


技术栈(固定,不问)

| 项 | 值 | |----|-----| | 框架 | uni-app + Vue3 + Vite + TypeScript | | 目标端 | 微信小程序(mp-weixin) | | 设计稿 | 750,rpx | | 请求 | uni.request 封装,无 dev proxy |

登录页默认必有(不依赖问题 4 会员),含手机号+验证码+本机号码一键登录。 Pinia:仅问题 4 选「是」时装;登录态 pinia-plugin-persistedstate + uni.setStorageSync

设计纪律(落地用户级 ~/.claude/CLAUDE.md §3(设计默认)):小程序受网络字体限制,不引 Google Fonts,改为系统字体堆叠 + token 化样式:

  • 字体:uni.scss 定义 --font-sans / --font-display,优先 PingFang SC / SF Pro Display,正文与标题用不同字重拉开层次,禁默认系统字号字重一锅煮
  • 配色:uni.scss 定义 --color-brand 非紫色阶(占位 #0EA5E9 系)+ 中性色阶,禁默认紫蓝渐变。
  • 背景:页面根容器渐变 + 装饰光斑(用 view + linear-gradient / radial-gradient),禁纯白纯灰大面积铺底。
  • 动效:按钮 :active 微缩放(transform: scale(0.98))+ transition 200ms,卡片点击有反馈。
  • rpx:全用 rpx,设计稿 750,标题字号 ≥ 36rpx,正文 ≥ 28rpx。

启动:问答收集需求(只此一轮,答完即开工)

AskUserQuestion 一次收齐 5 项,答完同一轮开始执行,禁止再确认。

| # | 问题 | 选项/说明 | |---|------|----------| | 1 | 小程序方向 | 商城 / 新闻 / 视频 / 其他(追问一句业务描述) | | 2 | 样式 | scss(默认) / css | | 3 | 接口域名 | 名称+prod 地址,有测试环境则加 test;未提供 → 按方向默认(见下表) | | 4 | 会员管理 | 是→Pinia+登录态+路由拦截;否→仅登录页骨架,token 用 uni.setStorageSync | | 5 | 项目名+目录 | 默认 ./ |

默认值:方向=商城,样式=scss。

方向 × 页面决策表(问答结束后按此生成 tabBar + 首页样板)

| 方向 | tabBar(不含登录) | 额外页面(占位) | 首页样板要点 | 域名默认(未提供时) | |------|-----------------|---------------|-------------|------------------| | 商城 | 首页 / 分类 / 购物车 / 我的 | category cart mine | 顶部搜索条 + 轮播 + 双列商品卡(图+标题+价) | main+user | | 新闻 | 首页 / 频道 / 我的 | channel mine | 置顶头条大卡 + 新闻列表(标题+来源+时间) | main+cms | | 视频 | 首页 / 发现 / 我的 | discover mine | 视频 feed(16:9 封面+播放量+标题) | main+media | | 其他 | 首页 / 我的 | mine | 渐变 hero + 2~3 功能入口卡(按用户描述命名) | main+user |

  • src/config/biz.ts 记录 bizType:'mall'|'news'|'video'|'custom'bizLabel,README 与报告写明方向。
  • 所有方向:登录页不进 tabBar;mine 页放头像/登录入口占位(会员=是接 store)。
  • tabBar 图标 static/tab-*.png 占位即可;pages.json 第一项为首页。

步骤 0:代码注释规范(开工前必读)

骨架代码须让后续接手的人不读 SKILL 也能懂。先看完本节再写任何文件。

风格铁律(4 条,与 ~/.claude/CLAUDE.md §5 输出风格一致;本 skill 细化约定)

  1. WHY 不 WHAT — 解释为什么这样写(约束、副作用、修改注意),不解释字面在做什么(变量/函数名能看懂的别注)。
  2. 中文为主,术语保留英文 — baseURL / token / envVersion / rpx / tabBar / openid 等不翻译。
  3. 文件头三段式职责 + 依赖方(谁读它) + 修改注意点(改这里同步改哪里),缺一不可。
  4. 禁废话注释 — "定义变量"/"调用函数"/"导入 store"/"创建 pinia" 一律不写。

必加注释文件清单(按生成顺序)

| 文件 | 必加内容 | 条件 | |------|---------|------| | src/config/design.ts | 文件头三段式(750 稿与 rpx 换算规则) | 必 | | src/config/biz.ts | 文件头三段式(方向 → tabBar/首页样板对应关系;改 type 同步 pages.json) | 必 | | src/config/env.ts | 文件头三段式 + IS_TEST / envVersion / API_MAP 切换逻辑 + 每个 key 业务含义 | 必 | | src/types/api.d.ts | ApiResponse 注「按后端实际结构调整」+ 字段含义 | 必 | | src/utils/message.ts | 文件头(为何统一封装 toast,禁页面裸 uni.showToast) | 必 | | src/api/request.ts | 文件头三段式 + 调用示例 + 请求/4001/401/loading/fail 分段标注 + 登录页 path | 必 | | src/api/modules/*.ts | 每个导出函数 JSDoc(@param/@returns + 业务说明,跨域名调用注明 api key) | 必 | | src/stores/user.ts | 文件头(持久化字段 / token 与 request header 的关系 / login/logout 副作用) | 会员=是 | | src/uni.scss | 颜色/字号/间距/字族各 token 区块分区注释;brand 注「占位色,按品牌替换」 | 必 | | src/pages/login/login.vue | 文件头 + 验证码倒计时 / 提交登录 / 一键登录解密 三段标注 + 待接位置 | 必 | | src/pages/index/index.vue | 文件头标注「按方向 {bizType} 生成的样板,可整体替换」 | 必 | | 其他 tab 页(mine/category 等) | 文件头一行说明职责 | 必 | | manifest.json | mp-weixin.appid 行旁注「上线前在微信公众平台申请并填入」(JSON 不能写注释 → 在 README 补) | 必 | | src/pages.json | JSON 不能注释 → 在 README「目录导读」表逐项列 pagePath 职责与 tabBar 归属 | 必 | | README.md | 「目录导读」表(含 pages 职责映射)+「设计纪律 cheatsheet」 | 必 |

风格示例(直接照抄)

1. 文件头三段式(所有 config / api / store / 页面必做)

/**
 * 业务方向 — 决定 tabBar 结构 与 首页样板形态(商城/新闻/视频/自定义)
 * 依赖方:pages.json tabBar.list 顺序、pages/index 样板、README 目录导读
 * 修改注意:改 type 须同步 pages.json + 各 tab 页占位 + 报告里的方向描述
 */

2. 接口函数 JSDoc(api/modules/*,跨域名注明 api key)

/**
 * 获取当前登录用户信息
 * @returns 用户基础资料 + 角色码;401/4001 由 request 拦截器统一跳 /pages/login/login,无需调用方处理
 */
export const getUserInfo = () => request({ api: 'user', url: 'user/info', loading: false })

3. request.ts 分段标注

// --- 请求前:拼 baseURL、附加 Authorization+token、可选 loading mask ---
// --- 4001 未登录:静默跳 /pages/login/login(避免重复跳已在登录页) ---
// --- 401 登录过期:清 token + 弹一次 modal 提示重新登录 ---
// --- code !== 0:业务失败 → showError(msg) 并 reject ---
// --- complete:统一 hideLoading,fail/success 都走 ---

4. pages/login/login.vue 流程分段


// --- 倒计时:60s 内禁用「获取验证码」,防重复点击;leaveBeforeUnmount 清 timer ---
// --- 提交登录:走 user 域名,成功写 token + setStorageSync,跳回上一页或首页 ---
// --- 一键登录:open-type=getPhoneNumber → wx.login code → 后端解密(待接,见 TODO) ---

5. manifest.jsonpages.json(JSON 不能注释 → README 目录导读补)

README 里固定一段:

## 目录导读
| 路径 | 职责 |
|------|------|
| manifest.json#mp-weixin.appid | 上线前在微信公众平台申请并填入,develop 模式可空跑 |
| src/pages.json#pages[0] index/index | 首页(按 {bizType} 生成的样板) |
| src/pages.json#tabBar.list | tabBar 顺序与首页/分类/购物车/我的对应,改了 biz.ts 须同步 |
| src/pages/login/login.vue | 登录页,**不进 tabBar**,被 4001/401 拦截器跳转目标 |

6. 禁止的废话注释(反例 vs 正例)

// ❌ 反例:在说"做了什么",删
// 导入 pinia
import { createPinia } from 'pinia'
// 创建 user store
export const useUserStore = defineStore(...)

// ✅ 正例:解释"为什么"
// token 走 setStorageSync 而非 pinia 内存:request header 同步读,小程序冷启动也要立即可用
const token = uni.getStorageSync('token')

执行清单(全部 ✓ 后再出报告)

□ 1. npx degit dcloudio/uni-preset-vue#vite-ts + npm install
□ 2. vite.config.ts 配 scss modern-compiler,消除 legacy-js-api 警告
□ 3. .gitignore 含 node_modules / dist / unpackage / .env.local
□ 4. src/config/{design,biz}.ts — 750 rpx + 业务方向
□ 5. src/uni.scss 设计 token: --color-brand(非紫) / --font-sans / --font-display / 字号阶 / 间距阶
□ 6. src/config/env.ts — API_MAP + IS_TEST + envVersion
□ 7. src/types/api.d.ts + src/utils/message.ts + src/api/request.ts
□ 8. 按方向生成 pages(首页样板 + login + tabBar 子页) + login 不进 tabBar
□ 9. src/pages.json pages + tabBar;manifest.json mp-weixin 基本配置
□ 10. 会员模块 stores/user.ts(若选是)
□ 11. 按「步骤 0」清单逐项加注释(文件头三段式 / request 4001-401-loading 分段 / 函数 JSDoc / login 流程分段 / pages.json+manifest.json 职责映射进 README),清单缺一即未通过
□ 12. 项目 README.md(含方向说明 + 目录导读 + 设计纪律 cheatsheet)
□ 13. npm run dev:mp-weixin + npm run build:mp-weixin 通过

步骤 1:脚手架 + 基础工程

脚手架

npx degit dcloudio/uni-preset-vue#vite-ts 
cd  && npm install

非交互;失败 → 换镜像 / --legacy-peer-deps / 删 lock 重装。

vite.config.ts(立刻改,消 Sass 警告)

import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'

export default defineConfig({
  plugins: [uni()],
  css: {
    preprocessorOptions: {
      scss: {
        api: 'modern-compiler',
        silenceDeprecations: ['legacy-js-api'],
      },
    },
  },
})

样式选 css 时可删 scss 配置。

package.json scripts(确认存在)

{
  "dev:mp-weixin": "uni -p mp-weixin",
  "build:mp-weixin": "uni build -p mp-weixin"
}

脚手架已有则不改;缺则补上。

目录约定

src/
  App.vue
  main.ts
  pages.json
  manifest.json
  uni.scss
  config/
    design.ts
    biz.ts
    env.ts
  types/
    api.d.ts
  utils/
    message.ts
  api/
    request.ts
    modules/example.ts
  pages/               # 按方向生成,商城示例:
    index/index.vue
    category/category.vue
    cart/cart.vue
    mine/mine.vue
    login/login.vue
  stores/              # 会员=是
    user.ts

index.html 为 uni-app H5 编译用,小程序运行不依赖它;保留脚手架原文件即可,勿当小程序入口。

依赖(按需装)

| 用途 | 包 | |------|-----| | 会员 | pinia + pinia-plugin-persistedstate(问题 4=是) |


步骤 2:设计稿适配

src/config/design.ts

/**
 * 设计稿约定 — 小程序统一 750 稿,样式单位 rpx
 * 设计稿标注 N px → 样式写 N rpx
 */
export const DESIGN = {
  designWidth: 750,
  unit: 'rpx',
} as const

样式全用 rpx(设计稿 32px → 32rpx);禁止 postcss-pxtorem。

src/config/biz.ts

/**
 * 业务方向 — 决定 tabBar 与首页样板(商城/新闻/视频/自定义)
 * 改 type 须同步 pages.json 与各 pages 占位
 */
export type BizType = 'mall' | 'news' | 'video' | 'custom'

export const BIZ = {
  type: 'mall' as BizType,
  label: '商城',
} as const

src/uni.scss(全局设计 token,默认必写)

落地用户级 ~/.claude/CLAUDE.md §3(设计默认):

/* ===== 颜色:占位 brand 非紫色阶,按品牌替换 ===== */
$color-brand-50:  #F0F9FF;
$color-brand-100: #E0F2FE;
$color-brand-500: #0EA5E9;
$color-brand-600: #0284C7;
$color-brand-700: #0369A1;
$color-text:      #0F172A;
$color-text-soft: #475569;
$color-bg:        #F8FAFC;

/* ===== 字号阶(rpx) ===== */
$fs-display: 56rpx;
$fs-h1:      44rpx;
$fs-h2:      36rpx;
$fs-body:    28rpx;
$fs-caption: 24rpx;

/* ===== 间距阶 ===== */
$space-1: 8rpx; $space-2: 16rpx; $space-3: 24rpx; $space-4: 32rpx; $space-6: 48rpx; $space-8: 64rpx;

/* ===== 圆角 ===== */
$radius-md: 16rpx;
$radius-lg: 24rpx;
$radius-xl: 32rpx;

/* ===== 字族:小程序无 Google Fonts,系统字体堆叠拉差异化 ===== */
:root {
  --font-sans:    "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Microsoft YaHei", sans-serif;
  --font-display: "PingFang SC", "SF Pro Display", -apple-system, BlinkMacSystemFont, sans-serif;
}
page { font-family: var(--font-sans); color: $color-text; background: $color-bg; }

字号、颜色、间距全部走 token,禁页面里硬编码颜色与字号。css 项目同样把这套 token 写进 app.vue 的全局 ``。


步骤 3:环境变量与拦截器

src/config/env.ts

/**
 * 小程序域名配置 — 无 dev proxy,直连 getApiBase
 * - IS_TEST: develop/trial 下切换测试/正式域名;release 恒走 prod
 * - 新增 api key:在本文件 API_MAP 加一项,request 里用 options.api 引用
 * - 上线前在微信公众平台配置 request 合法域名
 */
export const IS_TEST = true // develop/trial: true=测试 false=正式;release 忽略

export const API_MAP = {
  main:  { prod: 'https://api.example.com/api/',  test: 'https://api-test.example.com/api/' },
  user:  { prod: 'https://user.example.com/api/', test: 'https://user-test.example.com/api/' },
  wxapi: { prod: 'https://wxapi.example.com/api/' },
} as const

export type ApiKey = keyof typeof API_MAP

export function getEnvVersion(): 'develop' | 'trial' | 'release' {
  // @ts-expect-error 微信小程序运行时注入
  return __wxConfig?.envVersion ?? 'develop'
}

export function isDevOrTrial() {
  const v = getEnvVersion()
  return v === 'develop' || v === 'trial'
}

export function getApiBase(key: ApiKey): string {
  const c = API_MAP[key]
  return isDevOrTrial() && IS_TEST && 'test' in c && c.test ? c.test : c.prod
}

域名切换__wxConfig.envVersiondevelop / trial / release;release 恒 prod。

src/types/api.d.ts

export interface ApiResponse {
  code: number
  data: T
  msg: string
}

src/utils/message.ts

export function showError(msg: string) {
  uni.showToast({ title: msg, icon: 'none' })
}
export function showSuccess(msg: string) {
  uni.showToast({ title: msg, icon: 'success' })
}

src/api/request.ts

统一 request(options):

import { getApiBase, type ApiKey } from '@/config/env'
import type { ApiResponse } from '@/types/api'
import { showError } from '@/utils/message'

type RequestOptions = {
  api: ApiKey
  url: string
  method?: UniApp.RequestOptions['method']
  data?: Record
  loading?: string | boolean
  homeToken?: boolean
  token?: string
}

let tipLogin = 1

export function request(options: RequestOptions): Promise> {
  const baseURL = getApiBase(options.api)
  if (options.loading) {
    uni.showLoading({ title: typeof options.loading === 'string' ? options.loading : '加载中...', mask: true })
  }
  const token = options.token || uni.getStorageSync('token')
  let authorization = `Bearer ${token}`

  return new Promise((resolve, reject) => {
    uni.request({
      url: baseURL + options.url,
      method: options.method || 'GET',
      data: options.data,
      timeout: 15000,
      header: {
        Authorization: authorization,
        token: token || '',
        // 业务方按需启用:
        // UnionId: uni.getStorageSync('unionId') || '',
        // OpenId: uni.getStorageSync('openId') || '',
      },
      success(res) {
        const data = res.data as ApiResponse
        if (data.code === 4001 && !options.homeToken) {
          const pages = getCurrentPages()
          const route = pages[pages.length - 1]?.route
          if (route !== 'pages/login/login') {
            uni.navigateTo({ url: '/pages/login/login' })
          }
          return
        }
        if (data.code === 401 && !options.homeToken) {
          uni.removeStorageSync('token')
          if (tipLogin === 1) {
            tipLogin++
            uni.showModal({
              content: '您的登录已过期,请重新登录!',
              success(r) {
                if (r.confirm) uni.navigateTo({ url: '/pages/login/login' })
              },
            })
          }
          reject(data)
          return
        }
        if (data.code !== 0) {
          showError(data.msg || '请求失败')
          reject(data)
          return
        }
        resolve(data)
      },
      fail(err) {
        showError('请求失败')
        reject(err)
      },
      complete() {
        uni.hideLoading()
      },
    })
  })
}

无 dev proxy;特殊 api 的 Authorization 用 switch(options.api) 扩展。api/modules/example.ts 放跨域名调用示例。


步骤 4:登录页(默认必有)

src/pages/login/login.vue,不进 tabBar。须含:

  • 手机号 input + 验证码 input + 「获取验证码」按钮(倒计时占位)
  • 「登录」按钮 → request({ api: 'user', url: '...', method: 'POST' })
  • 「本机号码一键登录」— ``,解密逻辑注释待接

视觉要求(默认必做):

  • 顶部渐变 hero 区(linear-gradient from $color-brand-500 to $color-brand-700),内含 logo 占位 + 大字标题($fs-display + --font-display)+ 副标题($fs-body + $color-text-soft)
  • 表单区圆角卡片($radius-xl)悬浮在 hero 下方,白底 + 阴影 + 字段间距 $space-4
  • 主 CTA 按钮 :active { transform: scale(0.98); transition: transform 200ms },背景 $color-brand-500,字色白
  • 「一键登录」用次级样式(描边按钮),与主 CTA 形成层次

pages.json 注册路径,不要放进 tabBar.list


步骤 5:pages.json + tabBar + manifest

方向决策表生成 pagestabBar。登录页始终注册,不进 tabBar.list

商城 pages.json 示例

{
  "pages": [
    { "path": "pages/index/index", "style": { "navigationBarTitleText": "首页" } },
    { "path": "pages/category/category", "style": { "navigationBarTitleText": "分类" } },
    { "path": "pages/cart/cart", "style": { "navigationBarTitleText": "购物车" } },
    { "path": "pages/mine/mine", "style": { "navigationBarTitleText": "我的" } },
    { "path": "pages/login/login", "style": { "navigationBarTitleText": "登录" } }
  ],
  "tabBar": {
    "list": [
      { "pagePath": "pages/index/index", "text": "首页", "iconPath": "static/tab-home.png", "selectedIconPath": "static/tab-home-active.png" },
      { "pagePath": "pages/category/category", "text": "分类", "iconPath": "static/tab-category.png", "selectedIconPath": "static/tab-category-active.png" },
      { "pagePath": "pages/cart/cart", "text": "购物车", "iconPath": "static/tab-cart.png", "selectedIconPath": "static/tab-cart-active.png" },
      { "pagePath": "pages/mine/mine", "text": "我的", "iconPath": "static/tab-mine.png", "selectedIconPath": "static/tab-mine-active.png" }
    ]
  }
}

新闻 / 视频 / 其他按决策表替换 tab 项与页面 path;结构同上。

tabBar 图标用占位图;pages 数组第一项为首页。

首页与各 tab 页样板(按方向,禁裸 demo)

| 方向 | 首页 index | 其他 tab 页 | |------|-------------|------------| | 商城 | 搜索条 + 轮播 + 双列商品卡(图+标题+价,:active 缩放) | 分类:左侧类目+右侧商品列表;购物车:勾选+合计栏;我的:头像区+订单入口 | | 新闻 | 置顶头条大卡 + 新闻列表(标题+来源+时间) | 频道:频道 grid;我的:订阅/收藏入口 | | 视频 | 视频 feed(16:9 封面+播放量+标题,双列或单列) | 发现:话题/推荐 tab;我的:观看历史入口 | | 其他 | 渐变 hero + 2~3 功能入口卡(按 biz.label 命名) | 我的:通用用户中心占位 |

共用:uni.scss token、渐变/卡片样式、按钮 :active { transform: scale(0.98) }、字号≥2 层。

src/manifest.json

确认 mp-weixin 节点存在;appid 留空或占位,报告提醒用户填入。

会员=是:在 main.ts 注册 Pinia;需登录页在 onShow 或全局拦截检查 token。


步骤 6:会员模块(仅问题 4=是)

npm install pinia pinia-plugin-persistedstate

src/stores/user.tstoken/userInfo/isLogin;actions:login/logout/fetchUserInfo;持久化走 uni.setStorageSyncmain.ts:

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
// createApp 内: const pinia = createPinia(); pinia.use(piniaPluginPersistedstate); app.use(pinia)

步骤 7:项目 README.md

生成 25~45 行 README:

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.