微信小程序页面迁移校验之前 P5任务处理之前

This commit is contained in:
Neo
2026-03-09 01:19:21 +08:00
parent 263bf96035
commit 6e20987d2f
1112 changed files with 153824 additions and 219694 deletions

View File

@@ -0,0 +1,4 @@
{
"navigationBarTitleText": "开发调试面板",
"usingComponents": {}
}

View File

@@ -0,0 +1,182 @@
/**
* 开发调试面板页面
*
* 功能:
* - 展示当前用户上下文(角色、权限、绑定、门店)
* - 一键切换角色(后端真实修改 user_site_roles + 重签 token
* - 一键切换用户状态(后端真实修改 users.status + 重签 token
* - 页面跳转列表(点击跳转到任意已注册页面)
*/
import { request } from "../../utils/request"
// 页面列表分三段:正在迁移、已完成、未完成
const MIGRATING_PAGES = [
{ path: "pages/board-finance/board-finance", name: "财务看板" },
]
const DONE_PAGES = [
{ path: "pages/no-permission/no-permission", name: "无权限" },
{ path: "pages/login/login", name: "登录" },
{ path: "pages/apply/apply", name: "申请" },
{ path: "pages/reviewing/reviewing", name: "审核中" },
{ path: "pages/board-coach/board-coach", name: "助教看板" },
{ path: "pages/board-customer/board-customer", name: "客户看板" },
]
const TODO_PAGES = [
{ path: "pages/task-list/task-list", name: "任务列表" },
{ path: "pages/my-profile/my-profile", name: "个人中心" },
{ path: "pages/task-detail/task-detail", name: "任务详情" },
{ path: "pages/task-detail-callback/task-detail-callback", name: "任务-回访" },
{ path: "pages/task-detail-priority/task-detail-priority", name: "任务-优先级" },
{ path: "pages/task-detail-relationship/task-detail-relationship", name: "任务-关系" },
{ path: "pages/notes/notes", name: "备忘录" },
{ path: "pages/performance/performance", name: "业绩总览" },
{ path: "pages/performance-records/performance-records", name: "业绩明细" },
{ path: "pages/customer-detail/customer-detail", name: "客户详情" },
{ path: "pages/customer-service-records/customer-service-records", name: "客户服务记录" },
{ path: "pages/coach-detail/coach-detail", name: "助教详情" },
{ path: "pages/chat/chat", name: "AI 对话" },
{ path: "pages/chat-history/chat-history", name: "对话历史" },
{ path: "pages/index/index", name: "首页" },
{ path: "pages/mvp/mvp", name: "MVP" },
{ path: "pages/logs/logs", name: "日志" },
]
const ROLE_LIST = [
{ code: "coach", name: "助教" },
{ code: "staff", name: "员工" },
{ code: "site_admin", name: "店铺管理员" },
{ code: "tenant_admin", name: "租户管理员" },
]
const STATUS_LIST = ["new", "pending", "approved", "rejected", "disabled"]
Page({
data: {
ctx: null as any,
loading: true,
pages: [] as typeof TODO_PAGES,
migratingPages: MIGRATING_PAGES,
donePages: DONE_PAGES,
todoPages: TODO_PAGES,
roles: ROLE_LIST,
statuses: STATUS_LIST,
currentRole: "",
rolesText: "-",
permissionsText: "-",
bindingText: "-",
message: "",
messageType: "",
},
onShow() {
this.loadContext()
},
/** 加载当前用户调试上下文 */
async loadContext() {
// 没有 token 时不发请求,避免 401 → 刷新 → 跳转的无限循环
const token = wx.getStorageSync("token")
if (!token) {
this.setData({ loading: false })
this.showMsg("未登录,请先通过 dev-login 获取 token", "error")
return
}
this.setData({ loading: true, message: "" })
try {
const ctx = await request({ url: "/api/xcx/dev-context", method: "GET" })
const rolesText = ctx.roles?.length ? ctx.roles.join(", ") : "-"
const permissionsText = ctx.permissions?.length ? ctx.permissions.join(", ") : "-"
let bindingText = "-"
if (ctx.binding) {
const b = ctx.binding
bindingText = `${b.binding_type} (助教:${b.assistant_id || '-'} 员工:${b.staff_id || '-'})`
}
// 当前角色取第一个(通常只有一个)
const currentRole = ctx.roles?.length ? ctx.roles[0] : ""
this.setData({ ctx, rolesText, permissionsText, bindingText, currentRole, loading: false })
} catch (err: any) {
this.setData({ loading: false })
// 401 说明 token 无效或是受限令牌,不触发重试
const detail = err?.data?.detail || "网络错误"
this.showMsg("获取上下文失败: " + detail, "error")
}
},
/** 切换角色 */
async switchRole(e: any) {
const code = e.currentTarget.dataset.code
if (code === this.data.currentRole) return
wx.showLoading({ title: "切换中..." })
try {
const res = await request({
url: "/api/xcx/dev-switch-role",
method: "POST",
data: { role_code: code },
})
// 保存新 token
this.saveTokens(res)
this.showMsg(`已切换为 ${code}`, "success")
// 重新加载上下文
this.loadContext()
} catch (err: any) {
this.showMsg("切换角色失败: " + (err?.data?.detail || "网络错误"), "error")
} finally {
wx.hideLoading()
}
},
/** 切换用户状态 */
async switchStatus(e: any) {
const status = e.currentTarget.dataset.status
if (status === this.data.ctx?.status) return
wx.showLoading({ title: "切换中..." })
try {
const res = await request({
url: "/api/xcx/dev-switch-status",
method: "POST",
data: { status },
})
// 保存新 token
this.saveTokens(res)
this.showMsg(`状态已切换为 ${status}`, "success")
this.loadContext()
} catch (err: any) {
this.showMsg("切换状态失败: " + (err?.data?.detail || "网络错误"), "error")
} finally {
wx.hideLoading()
}
},
/** 跳转到指定页面 */
goPage(e: any) {
const url = "/" + e.currentTarget.dataset.url
// 使用 reLaunch 确保能跳转到任意页面(包括 tabBar 页面)
wx.reLaunch({ url })
},
/** 保存后端返回的新 token */
saveTokens(res: any) {
if (res.access_token && res.refresh_token) {
const app = getApp<IAppOption>()
app.globalData.token = res.access_token
app.globalData.refreshToken = res.refresh_token
wx.setStorageSync("token", res.access_token)
wx.setStorageSync("refreshToken", res.refresh_token)
if (res.user_id) {
wx.setStorageSync("userId", res.user_id)
}
if (res.user_status) {
wx.setStorageSync("userStatus", res.user_status)
}
}
},
/** 显示操作提示 */
showMsg(msg: string, type: "success" | "error") {
this.setData({ message: msg, messageType: type })
setTimeout(() => this.setData({ message: "" }), 3000)
},
})

View File

@@ -0,0 +1,109 @@
<!--
开发调试面板 — 页面跳转、角色切换、状态切换、绑定切换、上下文展示
-->
<view class="container">
<!-- 当前上下文信息 -->
<view class="section">
<view class="section-title">当前上下文</view>
<view class="info-card" wx:if="{{ctx}}">
<view class="info-row"><text class="label">user_id</text><text class="value">{{ctx.user_id}}</text></view>
<view class="info-row"><text class="label">openid</text><text class="value ellipsis">{{ctx.openid || '-'}}</text></view>
<view class="info-row"><text class="label">状态</text><text class="value tag tag-{{ctx.status}}">{{ctx.status}}</text></view>
<view class="info-row"><text class="label">昵称</text><text class="value">{{ctx.nickname || '-'}}</text></view>
<view class="info-row"><text class="label">门店</text><text class="value">{{ctx.site_name || '-'}} ({{ctx.site_id || '-'}})</text></view>
<view class="info-row"><text class="label">角色</text><text class="value">{{rolesText}}</text></view>
<view class="info-row"><text class="label">权限</text><text class="value ellipsis">{{permissionsText}}</text></view>
<view class="info-row"><text class="label">绑定</text><text class="value">{{bindingText}}</text></view>
</view>
<view class="info-card" wx:else>
<text class="hint">{{loading ? '加载中...' : '未登录或无法获取上下文'}}</text>
</view>
</view>
<!-- 角色切换 -->
<view class="section">
<view class="section-title">角色切换</view>
<view class="btn-group">
<view
wx:for="{{roles}}"
wx:key="code"
class="btn {{currentRole === item.code ? 'btn-active' : ''}}"
bindtap="switchRole"
data-code="{{item.code}}"
>{{item.name}}</view>
</view>
</view>
<!-- 用户状态切换 -->
<view class="section">
<view class="section-title">用户状态切换</view>
<view class="btn-group">
<view
wx:for="{{statuses}}"
wx:key="*this"
class="btn {{ctx.status === item ? 'btn-active' : ''}}"
bindtap="switchStatus"
data-status="{{item}}"
>{{item}}</view>
</view>
</view>
<!-- 页面跳转 -->
<view class="section">
<view class="section-title">🔧 正在迁移</view>
<view class="page-list">
<view
wx:for="{{migratingPages}}"
wx:key="path"
class="page-item page-item--migrating"
bindtap="goPage"
data-url="{{item.path}}"
>
<text class="page-name">{{item.name}}</text>
<text class="page-path">/{{item.path}}</text>
</view>
<view class="page-item page-item--empty" wx:if="{{migratingPages.length === 0}}">
<text class="hint">暂无</text>
</view>
</view>
</view>
<view class="section">
<view class="section-title">✅ 已完成</view>
<view class="page-list">
<view
wx:for="{{donePages}}"
wx:key="path"
class="page-item page-item--done"
bindtap="goPage"
data-url="{{item.path}}"
>
<text class="page-name">{{item.name}}</text>
<text class="page-path">/{{item.path}}</text>
</view>
</view>
</view>
<view class="section">
<view class="section-title">⏳ 未完成</view>
<view class="page-list">
<view
wx:for="{{todoPages}}"
wx:key="path"
class="page-item page-item--todo"
bindtap="goPage"
data-url="{{item.path}}"
>
<text class="page-name">{{item.name}}</text>
<text class="page-path">/{{item.path}}</text>
</view>
</view>
</view>
<!-- 操作提示 -->
<view class="section" wx:if="{{message}}">
<view class="message {{messageType}}">{{message}}</view>
</view>
</view>

View File

@@ -0,0 +1,171 @@
.container {
padding: 24rpx;
background: #f5f5f5;
min-height: 100vh;
}
.section {
margin-bottom: 32rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
padding-left: 8rpx;
border-left: 6rpx solid #1890ff;
}
.info-card {
background: #fff;
border-radius: 16rpx;
padding: 20rpx 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
.info-row {
display: flex;
align-items: center;
padding: 10rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.info-row:last-child {
border-bottom: none;
}
.label {
font-size: 24rpx;
color: #999;
width: 120rpx;
flex-shrink: 0;
}
.value {
font-size: 24rpx;
color: #333;
flex: 1;
word-break: break-all;
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 480rpx;
}
.hint {
font-size: 26rpx;
color: #999;
text-align: center;
padding: 20rpx 0;
}
/* 标签样式 */
.tag {
display: inline-block;
padding: 2rpx 12rpx;
border-radius: 8rpx;
font-size: 22rpx;
}
.tag-approved { background: #e6f7e6; color: #52c41a; }
.tag-pending { background: #fff7e6; color: #faad14; }
.tag-new { background: #e6f7ff; color: #1890ff; }
.tag-rejected { background: #fff1f0; color: #ff4d4f; }
.tag-disabled { background: #f5f5f5; color: #999; }
/* 按钮组 */
.btn-group {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.btn {
padding: 16rpx 28rpx;
background: #fff;
border: 2rpx solid #d9d9d9;
border-radius: 12rpx;
font-size: 26rpx;
color: #333;
text-align: center;
}
.btn-active {
background: #1890ff;
border-color: #1890ff;
color: #fff;
}
/* 页面列表 */
.page-list {
background: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
.page-item {
padding: 20rpx 24rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.page-item:last-child {
border-bottom: none;
}
.page-item:active {
background: #f0f5ff;
}
.page-item--migrating {
border-left: 6rpx solid #faad14;
}
.page-item--done {
border-left: 6rpx solid #52c41a;
}
.page-item--todo {
border-left: 6rpx solid #d9d9d9;
}
.page-item--empty {
text-align: center;
padding: 24rpx;
}
.page-name {
font-size: 28rpx;
color: #333;
font-weight: 500;
display: block;
}
.page-path {
font-size: 22rpx;
color: #999;
font-family: monospace;
display: block;
margin-top: 4rpx;
}
/* 消息提示 */
.message {
padding: 16rpx 24rpx;
border-radius: 12rpx;
font-size: 26rpx;
text-align: center;
}
.success {
background: #e6f7e6;
color: #52c41a;
}
.error {
background: #fff1f0;
color: #ff4d4f;
}