fix: 修复页面间状态丢失问题并优化选择页面

- 修复选择审批人后再选择抄送人导致状态丢失的问题
- 使用独立的 sessionStorage key (selectedApprovers, selectedCc)
- 组件每次挂载时从 sessionStorage 恢复状态
- 只在提交/保存成功后清除 sessionStorage
- 优化人员选择页面布局和样式
- 更新 CLAUDE.md 文档记录问题和解决方案

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-02-25 15:22:12 +08:00
parent 2d39357cc3
commit 09d4cb8054
51 changed files with 2944 additions and 647 deletions
@@ -0,0 +1,99 @@
<?php
namespace App\Controller;
use App\Service\ApprovalService;
use Webman\Http\Request;
use Webman\Http\Response;
class ApprovalController extends BaseController
{
private ApprovalService $service;
public function __construct()
{
$this->service = new ApprovalService();
}
/**
* 获取审批列表
*/
public function list(Request $request): Response
{
$userId = $this->getUserId($request);
$type = $request->get('type', 'my');
$page = (int) $request->get('page', 1);
$pageSize = (int) $request->get('page_size', 20);
$result = $this->service->getList($userId, $type, $page, $pageSize);
return $this->success($result);
}
/**
* 获取审批详情
*/
public function detail(Request $request): Response
{
$id = (int) $request->route('id');
$approval = \App\Model\ApprovalOrder::with(['flow', 'applicant', 'records'])
->findOrFail($id);
return $this->success($approval);
}
/**
* 审批通过
*/
public function approve(Request $request): Response
{
$userId = $this->getUserId($request);
$id = (int) $request->route('id');
$data = $request->post();
$data = $data ?? [];
$comment = $data['comment'] ?? null;
try {
$approval = $this->service->approve($id, $userId, $comment);
return $this->success($approval, '已通过');
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
/**
* 审批拒绝
*/
public function reject(Request $request): Response
{
$userId = $this->getUserId($request);
$id = (int) $request->route('id');
$data = $request->post();
$data = $data ?? [];
$comment = $data['comment'] ?? null;
try {
$approval = $this->service->reject($id, $userId, $comment);
return $this->success($approval, '已拒绝');
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
/**
* 撤销申请
*/
public function cancel(Request $request): Response
{
$userId = $this->getUserId($request);
$id = (int) $request->route('id');
try {
$approval = $this->service->cancel($id, $userId);
return $this->success($approval, '已撤销');
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
namespace App\Controller;
use App\Service\FeishuService;
use App\Util\JwtUtil;
use App\Model\User;
use App\Model\Company;
use Webman\Http\Request;
use Webman\Http\Response;
class AuthController extends BaseController
{
/**
* 用户登录
*/
public function login(Request $request): Response
{
$username = $request->post('username');
$password = $request->post('password');
// TODO: 实现真实的登录逻辑
if (!$username || !$password) {
return $this->error('用户名或密码不能为空');
}
// 模拟用户数据(用于测试)
$user = User::first();
if (!$user) {
return $this->error('用户不存在');
}
$token = JwtUtil::encode([
'user_id' => $user->id,
'company_id' => $user->company_id,
'username' => $user->name,
]);
return $this->success([
'token' => $token,
'user' => $user,
]);
}
/**
* 飞书免登
*/
public function feishuLogin(Request $request): Response
{
$authCode = $request->post('auth_code');
$tenantKey = $request->post('tenant_key');
if (!$authCode) {
return $this->error('缺少授权码');
}
try {
$feishuService = new FeishuService();
// 根据 tenant_key 获取公司
$company = $feishuService->getCompanyByTenantKey($tenantKey);
if (!$company) {
return $this->error('公司不存在');
}
// 设置公司并获取用户信息
$feishuService->setCompany($company->id);
$feishuData = $feishuService->getUserInfo($authCode);
// 创建或更新用户
$user = $feishuService->createOrUpdateUser($feishuData, $company->id);
// 生成 JWT Token
$token = JwtUtil::encode([
'user_id' => $user->id,
'company_id' => $user->company_id,
'feishu_user_id' => $user->feishu_user_id,
'union_id' => $user->union_id,
]);
return $this->success([
'token' => $token,
'user' => $user->load('company'),
]);
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
/**
* 获取当前用户信息
*/
public function me(Request $request): Response
{
$userId = $this->getUserId($request);
if (!$userId) {
return $this->error('未登录', 401);
}
$user = User::with('company')->find($userId);
if (!$user) {
return $this->error('用户不存在');
}
return $this->success($user);
}
}
+74
View File
@@ -0,0 +1,74 @@
<?php
namespace App\Controller;
use Webman\Http\Request;
use Webman\Http\Response;
abstract class BaseController
{
/**
* 成功响应
*/
protected function success($data = null, string $message = 'success'): Response
{
return json([
'code' => 0,
'message' => $message,
'data' => $data,
]);
}
/**
* 错误响应
*/
protected function error(string $message = 'error', int $code = 1, $data = null): Response
{
return json([
'code' => $code,
'message' => $message,
'data' => $data,
]);
}
/**
* 获取当前用户ID
*/
protected function getUserId(Request $request): ?int
{
// 先检查 request 中是否已注入
if (isset($request->userId)) {
return $request->userId;
}
// 从 Authorization header 获取 token
$token = $request->header('Authorization');
if ($token) {
$token = str_replace('Bearer ', '', $token);
}
if (!$token) {
return null;
}
// 解析 token
$payload = \App\Util\JwtUtil::decode($token);
if (!$payload) {
return null;
}
return (int) ($payload['user_id'] ?? null);
}
/**
* 验证认证并返回用户ID,失败则抛出异常
*/
protected function requireAuth(Request $request): int
{
$userId = $this->getUserId($request);
if (!$userId) {
throw new \Exception('未登录或登录已过期', 401);
}
return $userId;
}
}
@@ -0,0 +1,165 @@
<?php
namespace App\Controller;
use App\Service\BusinessTripService;
use Webman\Http\Request;
use Webman\Http\Response;
class BusinessTripController extends BaseController
{
private BusinessTripService $service;
public function __construct()
{
$this->service = new BusinessTripService();
}
/**
* 获取路由参数
*/
private function getRouteParam(Request $request, string $name, $default = null)
{
$route = $request->route;
if ($route && method_exists($route, 'param')) {
return $route->param($name) ?? $default;
}
return $default;
}
/**
* 获取出差申请列表
*/
public function list(Request $request): Response
{
$userId = $this->getUserId($request);
$type = $request->get('type', 'my');
$page = (int) $request->get('page', 1);
$pageSize = (int) $request->get('page_size', 20);
$result = $this->service->getList($userId, $type, $page, $pageSize);
return $this->success($result);
}
/**
* 获取出差申请详情
*/
public function detail(Request $request): Response
{
$id = (int) $this->getRouteParam($request, 'id');
if (!$id) {
return $this->error('参数错误');
}
$detail = $this->service->getDetail($id);
return $this->success($detail);
}
/**
* 创建出差申请(草稿)
*/
public function create(Request $request): Response
{
$userId = $this->getUserId($request);
$data = $request->post();
$data = $data ?? [];
// 验证必填字段
if (empty($data['reason'])) {
return $this->error('请输入出差原因');
}
if (empty($data['itineraries']) || !is_array($data['itineraries'])) {
return $this->error('请至少添加一条行程');
}
// 验证行程数据
foreach ($data['itineraries'] as $index => $itinerary) {
if (empty($itinerary['start_time'])) {
return $this->error("行程" . ($index + 1) . ":请选择开始时间");
}
if (empty($itinerary['destination'])) {
return $this->error("行程" . ($index + 1) . ":请输入目的地");
}
}
try {
$businessTrip = $this->service->createDraft($userId, $data);
return $this->success($businessTrip, '创建成功');
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
/**
* 更新出差申请
*/
public function update(Request $request): Response
{
$userId = $this->getUserId($request);
$id = (int) $this->getRouteParam($request, 'id');
if (!$id) {
return $this->error('参数错误');
}
$data = $request->post();
$data = $data ?? [];
try {
$businessTrip = $this->service->update($userId, $id, $data);
return $this->success($businessTrip, '更新成功');
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
/**
* 提交出差申请
*/
public function submit(Request $request): Response
{
$userId = $this->getUserId($request);
$id = (int) $this->getRouteParam($request, 'id');
if (!$id) {
return $this->error('参数错误');
}
$data = $request->post();
$data = $data ?? [];
$approvers = $data['approvers'] ?? [];
$ccUsers = $data['cc_users'] ?? [];
if (empty($approvers)) {
return $this->error('请选择审批人');
}
try {
$businessTrip = $this->service->submit($userId, $id, $approvers, $ccUsers);
return $this->success($businessTrip, '提交成功');
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
/**
* 取消出差申请
*/
public function cancel(Request $request): Response
{
$userId = $this->getUserId($request);
$id = (int) $this->getRouteParam($request, 'id');
if (!$id) {
return $this->error('参数错误');
}
try {
$businessTrip = $this->service->cancel($userId, $id);
return $this->success($businessTrip, '取消成功');
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
}
+92
View File
@@ -0,0 +1,92 @@
<?php
namespace App\Controller;
use App\Model\User;
use Webman\Http\Request;
use Webman\Http\Response;
class UserController extends BaseController
{
/**
* 获取用户信息
*/
public function info(Request $request): Response
{
$userId = $this->getUserId($request);
$user = User::find($userId);
if (!$user) {
return $this->error('用户不存在');
}
return $this->success($user);
}
/**
* 获取用户菜单权限
*/
public function menu(Request $request): Response
{
// TODO: 根据用户权限返回菜单
return $this->success([
['id' => 1, 'name' => '首页', 'path' => '/dashboard'],
['id' => 2, 'name' => '出差审批', 'path' => '/business-trip'],
]);
}
/**
* 获取用户可切换的公司列表
*/
public function companies(Request $request): Response
{
$userId = $this->getUserId($request);
// TODO: 从数据库获取用户关联的公司列表
return $this->success([
['id' => 1, 'name' => '公司A'],
]);
}
/**
* 切换当前公司
*/
public function switchCompany(Request $request): Response
{
$companyId = $request->post('company_id');
// TODO: 更新用户当前公司
return $this->success();
}
/**
* 获取用户列表(用于选择审批人等)
*/
public function list(Request $request): Response
{
$userId = $this->getUserId($request);
$keyword = $request->get('keyword', '');
$page = (int) $request->get('page', 1);
$pageSize = (int) $request->get('page_size', 50);
$query = User::active();
if ($keyword) {
$query->search($keyword);
}
$total = $query->count();
$list = $query->orderBy('id')
->offset(($page - 1) * $pageSize)
->limit($pageSize)
->get();
return $this->success([
'list' => $list,
'total' => $total,
'page' => $page,
'page_size' => $pageSize,
]);
}
}