# 校园快递代取小程序第一版 Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Build a native WeChat Mini Program demo that runs with a test AppID and lets a user create campus express pickup orders, view order details, and simulate order chat locally.

**Architecture:** Use a plain WeChat Mini Program scaffold with no external packages. Keep order validation and object creation in a pure utility module that can be tested with Node, and isolate WeChat local storage in a data store module so it can later be swapped for WeChat Cloud Development.

**Tech Stack:** WeChat Mini Program native JavaScript, WXML, WXSS, WeChat local storage APIs, Node `node:test` for utility verification.

---

## File Structure

- Create `project.config.json`: WeChat Developer Tools project settings with `touristappid`.
- Create `app.js`: Mini Program app bootstrap.
- Create `app.json`: page routing, tab bar, and window styling.
- Create `app.wxss`: global Apple-inspired visual system and shared utility classes.
- Create `sitemap.json`: allow page indexing configuration for the dev scaffold.
- Create `utils/order.js`: pure order and message helpers, status map, validation.
- Create `utils/store.js`: WeChat local storage wrapper for orders and messages.
- Create `tests/order.test.js`: Node tests for order validation and message creation.
- Create `pages/customer/index.*`: customer home, order entry, recent order list.
- Create `pages/order/form.*`: order form with validation and local persistence.
- Create `pages/order/detail.*`: order detail with chat entry.
- Create `pages/order/chat.*`: local simulated chat for customer/merchant messages.
- Create `pages/merchant/index.*`: merchant placeholder page with reserved order-handling surface.

## Task 1: Native Mini Program Shell

**Files:**
- Create: `project.config.json`
- Create: `app.js`
- Create: `app.json`
- Create: `app.wxss`
- Create: `sitemap.json`

- [ ] **Step 1: Create project configuration**

Create `project.config.json`:

```json
{
  "description": "校园快递代取小程序演示版",
  "packOptions": {
    "ignore": []
  },
  "setting": {
    "urlCheck": false,
    "es6": true,
    "enhance": true,
    "postcss": true,
    "minified": true
  },
  "compileType": "miniprogram",
  "libVersion": "latest",
  "appid": "touristappid",
  "projectname": "campus-express-miniapp",
  "condition": {}
}
```

- [ ] **Step 2: Create app route and tab configuration**

Create `app.json`:

```json
{
  "pages": [
    "pages/customer/index",
    "pages/order/form",
    "pages/order/detail",
    "pages/order/chat",
    "pages/merchant/index"
  ],
  "window": {
    "navigationBarTitleText": "校园快递代取",
    "navigationBarBackgroundColor": "#f5f5f7",
    "navigationBarTextStyle": "black",
    "backgroundColor": "#f5f5f7"
  },
  "tabBar": {
    "color": "#8e8e93",
    "selectedColor": "#007aff",
    "backgroundColor": "#ffffff",
    "borderStyle": "white",
    "list": [
      {
        "pagePath": "pages/customer/index",
        "text": "客户"
      },
      {
        "pagePath": "pages/merchant/index",
        "text": "商家"
      }
    ]
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}
```

- [ ] **Step 3: Create app bootstrap**

Create `app.js`:

```js
App({
  globalData: {
    appName: '校园快递代取'
  }
})
```

- [ ] **Step 4: Create global styles**

Create `app.wxss`:

```css
page {
  min-height: 100%;
  background: #f5f5f7;
  color: #1d1d1f;
  font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
  letter-spacing: 0;
}

.page {
  min-height: 100vh;
  box-sizing: border-box;
  padding: 32rpx 28rpx 48rpx;
}

.section {
  margin-top: 28rpx;
}

.panel {
  background: #ffffff;
  border: 1rpx solid rgba(0, 0, 0, 0.04);
  border-radius: 24rpx;
  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.04);
}

.muted {
  color: #86868b;
}

.primary-button {
  height: 96rpx;
  border-radius: 20rpx;
  background: #007aff;
  color: #ffffff;
  font-size: 32rpx;
  font-weight: 600;
  line-height: 96rpx;
}

.primary-button::after {
  border: 0;
}
```

- [ ] **Step 5: Create sitemap**

Create `sitemap.json`:

```json
{
  "rules": [
    {
      "action": "allow",
      "page": "*"
    }
  ]
}
```

- [ ] **Step 6: Commit shell**

Run:

```bash
git add project.config.json app.js app.json app.wxss sitemap.json
git commit -m "feat: add mini program shell"
```

Expected: commit succeeds.

## Task 2: Order Domain Helpers

**Files:**
- Create: `utils/order.js`
- Create: `tests/order.test.js`

- [ ] **Step 1: Write failing tests**

Create `tests/order.test.js`:

```js
const test = require('node:test')
const assert = require('node:assert/strict')
const {
  STATUS_TEXT,
  validateOrderInput,
  createOrder,
  createMessage,
  formatDateTime
} = require('../utils/order')

test('validateOrderInput reports missing required pickup fields', () => {
  const result = validateOrderInput({
    station: '',
    pickupCode: '',
    receiverName: '李同学',
    phone: '13800138000',
    building: '3号宿舍楼'
  })

  assert.deepEqual(result, {
    valid: false,
    message: '请填写快递站点'
  })
})

test('createOrder trims fields and defaults to pending status', () => {
  const order = createOrder(
    {
      station: ' 菜鸟驿站东门店 ',
      pickupCode: ' A12-34 ',
      company: ' 顺丰 ',
      packageName: ' 文件 ',
      receiverName: ' 李同学 ',
      phone: ' 13800138000 ',
      building: ' 3号宿舍楼 ',
      room: ' 502 ',
      note: ' 晚上送 '
    },
    {
      id: 'order_1',
      date: new Date('2026-08-28T10:30:00+08:00')
    }
  )

  assert.equal(order.id, 'order_1')
  assert.equal(order.status, 'pending')
  assert.equal(order.statusText, STATUS_TEXT.pending)
  assert.equal(order.station, '菜鸟驿站东门店')
  assert.equal(order.pickupCode, 'A12-34')
  assert.equal(order.createdAt, '2026-08-28 10:30')
  assert.equal(order.updatedAt, '2026-08-28 10:30')
})

test('createMessage trims content and records sender role', () => {
  const message = createMessage('order_1', 'merchant', ' 已收到，马上处理 ', {
    id: 'message_1',
    date: new Date('2026-08-28T10:35:00+08:00')
  })

  assert.deepEqual(message, {
    id: 'message_1',
    orderId: 'order_1',
    senderRole: 'merchant',
    content: '已收到，马上处理',
    createdAt: '2026-08-28 10:35'
  })
})

test('formatDateTime pads month, day, hour, and minute', () => {
  assert.equal(formatDateTime(new Date('2026-01-02T03:04:00+08:00')), '2026-01-02 03:04')
})
```

- [ ] **Step 2: Run tests to verify failure**

Run:

```bash
node --test tests/order.test.js
```

Expected: FAIL because `utils/order.js` does not exist.

- [ ] **Step 3: Implement order helpers**

Create `utils/order.js`:

```js
const STATUS_TEXT = {
  pending: '待接单',
  accepted: '已接单',
  picking: '取件中',
  delivering: '配送中',
  completed: '已送达',
  exception: '异常'
}

function pad(value) {
  return String(value).padStart(2, '0')
}

function formatDateTime(date = new Date()) {
  const year = date.getFullYear()
  const month = pad(date.getMonth() + 1)
  const day = pad(date.getDate())
  const hour = pad(date.getHours())
  const minute = pad(date.getMinutes())
  return `${year}-${month}-${day} ${hour}:${minute}`
}

function createId(prefix) {
  return `${prefix}_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`
}

function clean(value) {
  return String(value || '').trim()
}

function validateOrderInput(input) {
  const checks = [
    ['station', '请填写快递站点'],
    ['pickupCode', '请填写取件码'],
    ['receiverName', '请填写收货人姓名'],
    ['phone', '请填写联系电话'],
    ['building', '请填写配送地点']
  ]

  for (const [field, message] of checks) {
    if (!clean(input[field])) {
      return { valid: false, message }
    }
  }

  return { valid: true, message: '' }
}

function createOrder(input, options = {}) {
  const createdAt = formatDateTime(options.date || new Date())
  return {
    id: options.id || createId('order'),
    status: 'pending',
    statusText: STATUS_TEXT.pending,
    station: clean(input.station),
    pickupCode: clean(input.pickupCode),
    company: clean(input.company),
    packageName: clean(input.packageName),
    receiverName: clean(input.receiverName),
    phone: clean(input.phone),
    building: clean(input.building),
    room: clean(input.room),
    note: clean(input.note),
    createdAt,
    updatedAt: createdAt
  }
}

function createMessage(orderId, senderRole, content, options = {}) {
  return {
    id: options.id || createId('message'),
    orderId,
    senderRole,
    content: clean(content),
    createdAt: formatDateTime(options.date || new Date())
  }
}

module.exports = {
  STATUS_TEXT,
  formatDateTime,
  validateOrderInput,
  createOrder,
  createMessage
}
```

- [ ] **Step 4: Run tests to verify pass**

Run:

```bash
node --test tests/order.test.js
```

Expected: PASS for all four tests.

- [ ] **Step 5: Commit order helpers**

Run:

```bash
git add utils/order.js tests/order.test.js
git commit -m "feat: add order domain helpers"
```

Expected: commit succeeds.

## Task 3: Local Store Adapter

**Files:**
- Create: `utils/store.js`

- [ ] **Step 1: Implement store adapter**

Create `utils/store.js`:

```js
const { createOrder, createMessage } = require('./order')

const ORDERS_KEY = 'campus_express_orders'

function read(key, fallback) {
  try {
    const value = wx.getStorageSync(key)
    return value || fallback
  } catch (error) {
    return fallback
  }
}

function write(key, value) {
  try {
    wx.setStorageSync(key, value)
    return true
  } catch (error) {
    return false
  }
}

function messageKey(orderId) {
  return `campus_express_messages_${orderId}`
}

function getOrders() {
  return read(ORDERS_KEY, [])
}

function getOrder(orderId) {
  return getOrders().find((order) => order.id === orderId) || null
}

function addOrder(input) {
  const order = createOrder(input)
  const orders = [order].concat(getOrders())
  write(ORDERS_KEY, orders)
  return order
}

function getMessages(orderId) {
  return read(messageKey(orderId), [])
}

function addMessage(orderId, senderRole, content) {
  const message = createMessage(orderId, senderRole, content)
  const messages = getMessages(orderId).concat(message)
  write(messageKey(orderId), messages)
  return message
}

module.exports = {
  getOrders,
  getOrder,
  addOrder,
  getMessages,
  addMessage
}
```

- [ ] **Step 2: Commit store adapter**

Run:

```bash
git add utils/store.js
git commit -m "feat: add local store adapter"
```

Expected: commit succeeds.

## Task 4: Customer Order Flow Pages

**Files:**
- Create: `pages/customer/index.js`
- Create: `pages/customer/index.wxml`
- Create: `pages/customer/index.wxss`
- Create: `pages/customer/index.json`
- Create: `pages/order/form.js`
- Create: `pages/order/form.wxml`
- Create: `pages/order/form.wxss`
- Create: `pages/order/form.json`
- Create: `pages/order/detail.js`
- Create: `pages/order/detail.wxml`
- Create: `pages/order/detail.wxss`
- Create: `pages/order/detail.json`

- [ ] **Step 1: Build customer home files**

Create `pages/customer/index.js`:

```js
const store = require('../../utils/store')

Page({
  data: {
    orders: []
  },

  onShow() {
    this.setData({
      orders: store.getOrders()
    })
  },

  goCreate() {
    wx.navigateTo({
      url: '/pages/order/form'
    })
  },

  openOrder(event) {
    const { id } = event.currentTarget.dataset
    wx.navigateTo({
      url: `/pages/order/detail?id=${id}`
    })
  }
})
```

Create `pages/customer/index.wxml`:

```xml
<view class="page customer-page">
  <view class="hero">
    <view>
      <text class="eyebrow">Campus Express</text>
      <text class="title">校园快递代取</text>
      <text class="subtitle">填写取件码和宿舍信息，先把完整下单流程跑起来。</text>
    </view>
    <button class="primary-button hero-button" bindtap="goCreate">立即下单</button>
  </view>

  <view class="section section-heading">
    <text class="section-title">我的订单</text>
    <text class="section-count">{{orders.length}} 单</text>
  </view>

  <view wx:if="{{orders.length === 0}}" class="empty panel">
    <text class="empty-title">还没有订单</text>
    <text class="empty-text">创建一单后，这里会显示订单状态和配送信息。</text>
  </view>

  <view wx:else class="order-list">
    <view
      wx:for="{{orders}}"
      wx:key="id"
      class="order-card panel"
      data-id="{{item.id}}"
      bindtap="openOrder"
    >
      <view class="order-card-top">
        <view>
          <text class="order-station">{{item.station}}</text>
          <text class="order-time">{{item.createdAt}}</text>
        </view>
        <text class="status-pill">{{item.statusText}}</text>
      </view>
      <view class="order-info">
        <text>取件码 {{item.pickupCode}}</text>
        <text>{{item.building}}{{item.room ? ' · ' + item.room : ''}}</text>
      </view>
    </view>
  </view>
</view>
```

Create `pages/customer/index.wxss` with the same visual system used in the implementation.

Create `pages/customer/index.json`:

```json
{
  "navigationBarTitleText": "客户"
}
```

- [ ] **Step 2: Build order form files**

Create `pages/order/form.js` using `validateOrderInput` and `store.addOrder`. It should show a toast for missing required fields and navigate to the detail page after saving.

Create `pages/order/form.wxml` with two form sections: express information and delivery information.

Create `pages/order/form.wxss` with clean input rows and one fixed-width primary submit button.

Create `pages/order/form.json`:

```json
{
  "navigationBarTitleText": "创建订单"
}
```

- [ ] **Step 3: Build order detail files**

Create `pages/order/detail.js`:

```js
const store = require('../../utils/store')

Page({
  data: {
    order: null
  },

  onLoad(options) {
    this.orderId = options.id
  },

  onShow() {
    this.setData({
      order: store.getOrder(this.orderId)
    })
  },

  openChat() {
    wx.navigateTo({
      url: `/pages/order/chat?id=${this.orderId}`
    })
  }
})
```

Create `pages/order/detail.wxml` with status, pickup information, delivery information, and chat button.

Create `pages/order/detail.wxss` with Apple-inspired panels and compact detail rows.

Create `pages/order/detail.json`:

```json
{
  "navigationBarTitleText": "订单详情"
}
```

- [ ] **Step 4: Commit customer order flow**

Run:

```bash
git add pages/customer pages/order/form.* pages/order/detail.*
git commit -m "feat: add customer order flow"
```

Expected: commit succeeds.

## Task 5: Chat Page

**Files:**
- Create: `pages/order/chat.js`
- Create: `pages/order/chat.wxml`
- Create: `pages/order/chat.wxss`
- Create: `pages/order/chat.json`

- [ ] **Step 1: Build chat logic**

Create `pages/order/chat.js` with local message loading, a `customer` / `merchant` sender switch, input binding, validation, and `store.addMessage`.

- [ ] **Step 2: Build chat view**

Create `pages/order/chat.wxml` showing message bubbles aligned by sender role, a compact sender switch, and a bottom input bar.

- [ ] **Step 3: Build chat styles**

Create `pages/order/chat.wxss` with customer messages in blue bubbles and merchant messages in white bubbles.

- [ ] **Step 4: Create page config**

Create `pages/order/chat.json`:

```json
{
  "navigationBarTitleText": "订单沟通"
}
```

- [ ] **Step 5: Commit chat page**

Run:

```bash
git add pages/order/chat.*
git commit -m "feat: add order chat page"
```

Expected: commit succeeds.

## Task 6: Merchant Placeholder

**Files:**
- Create: `pages/merchant/index.js`
- Create: `pages/merchant/index.wxml`
- Create: `pages/merchant/index.wxss`
- Create: `pages/merchant/index.json`

- [ ] **Step 1: Build merchant page**

Create a merchant page that reads local order count, displays reserved modules for order list, accepting, status updates, and customer chat, and keeps the same visual language as the customer side.

- [ ] **Step 2: Create page config**

Create `pages/merchant/index.json`:

```json
{
  "navigationBarTitleText": "商家"
}
```

- [ ] **Step 3: Commit merchant placeholder**

Run:

```bash
git add pages/merchant
git commit -m "feat: add merchant placeholder"
```

Expected: commit succeeds.

## Task 7: Verification And Documentation

**Files:**
- Create: `README.md`

- [ ] **Step 1: Write README**

Create `README.md` with setup instructions:

```markdown
# 校园快递代取小程序

第一版是微信小程序本地演示版，用于跑通客户下单、订单详情、订单沟通和商家端预留流程。

## 运行方式

1. 安装并打开微信开发者工具。
2. 选择“导入项目”。
3. 项目目录选择当前文件夹。
4. AppID 使用测试号或保持 `touristappid`。
5. 编译后从“客户”页开始体验。

## 当前能力

- 客户创建快递代取订单。
- 客户查看订单列表和订单详情。
- 订单沟通窗口支持本地模拟客户/商家对话。
- 商家端接单页面已预留。

## 数据说明

第一版使用微信本地缓存保存订单和消息，不需要服务器。换电脑、清理缓存或更换设备后，本地数据不会同步。
```

- [ ] **Step 2: Run unit tests**

Run:

```bash
node --test tests/order.test.js
```

Expected: PASS.

- [ ] **Step 3: Validate required files exist**

Run:

```bash
test -f project.config.json &&
test -f app.json &&
test -f pages/customer/index.wxml &&
test -f pages/order/form.wxml &&
test -f pages/order/detail.wxml &&
test -f pages/order/chat.wxml &&
test -f pages/merchant/index.wxml
```

Expected: command exits with status 0.

- [ ] **Step 4: Commit README and verification**

Run:

```bash
git add README.md
git commit -m "docs: add miniapp usage guide"
```

Expected: commit succeeds.

## Self-Review

- Spec coverage: customer order creation, order list, order detail, chat, merchant placeholder, local storage, validation, and Apple-inspired UI are covered by Tasks 1-7.
- Placeholder scan: no unresolved placeholder markers or unspecified implementation steps remain.
- Type consistency: order and message field names match the design document and are reused by pages through `utils/order.js` and `utils/store.js`.
