test-stage-navigation.html 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>测试项目阶段自动导航</title>
  7. <script src="https://unpkg.com/parse@4.2.0/dist/parse.min.js"></script>
  8. <style>
  9. * {
  10. margin: 0;
  11. padding: 0;
  12. box-sizing: border-box;
  13. }
  14. body {
  15. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  16. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  17. min-height: 100vh;
  18. display: flex;
  19. align-items: center;
  20. justify-content: center;
  21. padding: 20px;
  22. }
  23. .container {
  24. background: white;
  25. border-radius: 16px;
  26. box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
  27. padding: 40px;
  28. max-width: 800px;
  29. width: 100%;
  30. }
  31. h1 {
  32. font-size: 28px;
  33. color: #1a202c;
  34. margin-bottom: 8px;
  35. display: flex;
  36. align-items: center;
  37. gap: 12px;
  38. }
  39. .subtitle {
  40. color: #718096;
  41. font-size: 14px;
  42. margin-bottom: 32px;
  43. }
  44. .section {
  45. margin-bottom: 32px;
  46. }
  47. .section-title {
  48. font-size: 16px;
  49. font-weight: 600;
  50. color: #2d3748;
  51. margin-bottom: 16px;
  52. display: flex;
  53. align-items: center;
  54. gap: 8px;
  55. }
  56. .form-group {
  57. margin-bottom: 16px;
  58. }
  59. label {
  60. display: block;
  61. font-size: 14px;
  62. font-weight: 500;
  63. color: #4a5568;
  64. margin-bottom: 8px;
  65. }
  66. input, select {
  67. width: 100%;
  68. padding: 12px 16px;
  69. border: 2px solid #e2e8f0;
  70. border-radius: 8px;
  71. font-size: 14px;
  72. transition: all 0.3s;
  73. }
  74. input:focus, select:focus {
  75. outline: none;
  76. border-color: #667eea;
  77. box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
  78. }
  79. .btn {
  80. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  81. color: white;
  82. border: none;
  83. padding: 14px 28px;
  84. border-radius: 8px;
  85. font-size: 16px;
  86. font-weight: 600;
  87. cursor: pointer;
  88. transition: all 0.3s;
  89. width: 100%;
  90. display: flex;
  91. align-items: center;
  92. justify-content: center;
  93. gap: 8px;
  94. }
  95. .btn:hover {
  96. transform: translateY(-2px);
  97. box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
  98. }
  99. .btn:disabled {
  100. opacity: 0.6;
  101. cursor: not-allowed;
  102. transform: none;
  103. }
  104. .project-card {
  105. background: #f7fafc;
  106. border: 2px solid #e2e8f0;
  107. border-radius: 8px;
  108. padding: 16px;
  109. margin-bottom: 12px;
  110. cursor: pointer;
  111. transition: all 0.3s;
  112. }
  113. .project-card:hover {
  114. border-color: #667eea;
  115. background: #edf2f7;
  116. transform: translateX(4px);
  117. }
  118. .project-title {
  119. font-weight: 600;
  120. color: #2d3748;
  121. margin-bottom: 8px;
  122. }
  123. .project-meta {
  124. display: flex;
  125. gap: 16px;
  126. font-size: 13px;
  127. color: #718096;
  128. }
  129. .stage-badge {
  130. display: inline-block;
  131. padding: 4px 12px;
  132. border-radius: 12px;
  133. font-size: 12px;
  134. font-weight: 600;
  135. }
  136. .stage-order {
  137. background: #fee;
  138. color: #c53030;
  139. }
  140. .stage-requirements {
  141. background: #fef5e7;
  142. color: #d68910;
  143. }
  144. .stage-delivery {
  145. background: #ebf8ff;
  146. color: #2c5282;
  147. }
  148. .stage-aftercare {
  149. background: #f0fff4;
  150. color: #276749;
  151. }
  152. .log {
  153. background: #1a202c;
  154. color: #48bb78;
  155. font-family: 'Courier New', monospace;
  156. font-size: 13px;
  157. padding: 16px;
  158. border-radius: 8px;
  159. max-height: 300px;
  160. overflow-y: auto;
  161. line-height: 1.6;
  162. }
  163. .log-item {
  164. margin-bottom: 4px;
  165. }
  166. .log-success { color: #48bb78; }
  167. .log-error { color: #f56565; }
  168. .log-info { color: #63b3ed; }
  169. .log-warning { color: #ed8936; }
  170. .loading {
  171. display: inline-block;
  172. width: 16px;
  173. height: 16px;
  174. border: 2px solid rgba(255, 255, 255, 0.3);
  175. border-radius: 50%;
  176. border-top-color: white;
  177. animation: spin 0.8s linear infinite;
  178. }
  179. @keyframes spin {
  180. to { transform: rotate(360deg); }
  181. }
  182. </style>
  183. </head>
  184. <body>
  185. <div class="container">
  186. <h1>
  187. <span>🧪</span>
  188. 项目阶段自动导航测试
  189. </h1>
  190. <p class="subtitle">测试从不同阶段进入项目时是否自动跳转到对应阶段</p>
  191. <div class="section">
  192. <div class="section-title">
  193. <span>📋</span>
  194. 选择测试项目
  195. </div>
  196. <div class="form-group">
  197. <label>公司 ID (cid)</label>
  198. <input type="text" id="cid" value="cDL6R1hgSi" placeholder="输入公司ID">
  199. </div>
  200. <button class="btn" onclick="loadProjects()">
  201. <span>🔍</span>
  202. <span id="loadBtnText">加载项目列表</span>
  203. </button>
  204. </div>
  205. <div class="section" id="projectsSection" style="display: none;">
  206. <div class="section-title">
  207. <span>📂</span>
  208. 项目列表
  209. </div>
  210. <div id="projectsList"></div>
  211. </div>
  212. <div class="section">
  213. <div class="section-title">
  214. <span>📝</span>
  215. 测试日志
  216. </div>
  217. <div class="log" id="log"></div>
  218. </div>
  219. </div>
  220. <script>
  221. // 初始化 Parse
  222. Parse.initialize("nova");
  223. Parse.serverURL = 'https://parse.fmode.cn';
  224. const cid = 'cDL6R1hgSi';
  225. function log(message, type = 'info') {
  226. const logEl = document.getElementById('log');
  227. const time = new Date().toLocaleTimeString();
  228. const className = `log-${type}`;
  229. logEl.innerHTML += `<div class="log-item ${className}">[${time}] ${message}</div>`;
  230. logEl.scrollTop = logEl.scrollHeight;
  231. }
  232. function clearLog() {
  233. document.getElementById('log').innerHTML = '';
  234. }
  235. async function loadProjects() {
  236. const cidInput = document.getElementById('cid').value;
  237. if (!cidInput) {
  238. log('❌ 请输入公司ID', 'error');
  239. return;
  240. }
  241. clearLog();
  242. log('🔄 开始加载项目列表...', 'info');
  243. const btn = document.querySelector('.btn');
  244. const btnText = document.getElementById('loadBtnText');
  245. btn.disabled = true;
  246. btnText.innerHTML = '<span class="loading"></span> 加载中...';
  247. try {
  248. const query = new Parse.Query('Project');
  249. query.equalTo('company', cidInput);
  250. query.notEqualTo('isDeleted', true);
  251. query.include('contact', 'assignee');
  252. query.descending('updatedAt');
  253. query.limit(20);
  254. const projects = await query.find();
  255. log(`✅ 成功加载 ${projects.length} 个项目`, 'success');
  256. const projectsList = document.getElementById('projectsList');
  257. projectsList.innerHTML = '';
  258. if (projects.length === 0) {
  259. projectsList.innerHTML = '<p style="text-align:center;color:#718096;">没有找到项目</p>';
  260. } else {
  261. projects.forEach(project => {
  262. const title = project.get('title') || '未命名项目';
  263. const currentStage = project.get('currentStage') || '订单分配';
  264. const customer = project.get('contact')?.get('realname') || project.get('contact')?.get('name') || '未知客户';
  265. let stageClass = 'stage-order';
  266. if (currentStage.includes('需求')) stageClass = 'stage-requirements';
  267. else if (currentStage.includes('交付') || currentStage.includes('执行')) stageClass = 'stage-delivery';
  268. else if (currentStage.includes('售后') || currentStage.includes('归档')) stageClass = 'stage-aftercare';
  269. const card = document.createElement('div');
  270. card.className = 'project-card';
  271. card.onclick = () => testNavigation(project.id, currentStage, cidInput);
  272. card.innerHTML = `
  273. <div class="project-title">${title}</div>
  274. <div class="project-meta">
  275. <span><strong>阶段:</strong> <span class="stage-badge ${stageClass}">${currentStage}</span></span>
  276. <span><strong>客户:</strong> ${customer}</span>
  277. <span><strong>ID:</strong> ${project.id}</span>
  278. </div>
  279. `;
  280. projectsList.appendChild(card);
  281. });
  282. }
  283. document.getElementById('projectsSection').style.display = 'block';
  284. } catch (error) {
  285. log(`❌ 加载失败: ${error.message}`, 'error');
  286. console.error(error);
  287. } finally {
  288. btn.disabled = false;
  289. btnText.textContent = '重新加载项目列表';
  290. }
  291. }
  292. function testNavigation(projectId, currentStage, cidValue) {
  293. log(`\n🚀 测试项目: ${projectId}`, 'info');
  294. log(`📍 当前阶段: ${currentStage}`, 'info');
  295. // 阶段映射
  296. const stageMap = {
  297. '订单分配': 'order',
  298. '确认需求': 'requirements',
  299. '方案确认': 'requirements',
  300. '方案深化': 'requirements',
  301. '交付执行': 'delivery',
  302. '建模': 'delivery',
  303. '软装': 'delivery',
  304. '渲染': 'delivery',
  305. '后期': 'delivery',
  306. '尾款结算': 'aftercare',
  307. '客户评价': 'aftercare',
  308. '投诉处理': 'aftercare',
  309. '售后归档': 'aftercare'
  310. };
  311. const targetStage = stageMap[currentStage] || 'order';
  312. log(`🎯 预期跳转: ${targetStage}`, 'warning');
  313. const baseUrl = `http://localhost:4200/wxwork/${cidValue}/project/${projectId}`;
  314. const targetUrl = `${baseUrl}/${targetStage}`;
  315. log(`📋 基础URL: ${baseUrl}`, 'info');
  316. log(`✅ 目标URL: ${targetUrl}`, 'success');
  317. log(`⏰ 3秒后在新标签页打开...`, 'warning');
  318. setTimeout(() => {
  319. window.open(targetUrl, '_blank');
  320. log(`✅ 已在新标签页打开项目`, 'success');
  321. log(`💡 请在新标签页中检查:`, 'info');
  322. log(` 1. URL 是否包含 /${targetStage}`, 'info');
  323. log(` 2. 页面是否显示 "${currentStage}" 阶段内容`, 'info');
  324. log(` 3. 控制台是否有 "📍 项目当前阶段" 日志`, 'info');
  325. }, 3000);
  326. }
  327. // 页面加载时自动加载项目
  328. window.onload = () => {
  329. log('🎉 测试页面已加载', 'success');
  330. log('💡 点击"加载项目列表"开始测试', 'info');
  331. };
  332. </script>
  333. </body>
  334. </html>