lineage.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. const app = getApp();
  2. Page({
  3. data: {
  4. searchKeyword: '',
  5. searchResults: [],
  6. center: null,
  7. reversedGenerations: [],
  8. siblings: [],
  9. peers: [],
  10. peerScrollLeft: 0,
  11. children: [],
  12. childrenScrollLeft: 0,
  13. loading: false,
  14. showDetail: false,
  15. detailMember: null
  16. },
  17. onLoad: function () {},
  18. onShow: function () {
  19. if (typeof this.getTabBar === 'function' && this.getTabBar()) {
  20. this.getTabBar().setData({ selected: 1 });
  21. }
  22. },
  23. onSearchInput: function (e) {
  24. this.setData({ searchKeyword: e.detail.value });
  25. },
  26. searchMember: function () {
  27. const kw = this.data.searchKeyword.trim();
  28. if (!kw) {
  29. wx.showToast({ title: '请输入姓名', icon: 'none' });
  30. return;
  31. }
  32. if (!app.globalData.isLoggedIn) {
  33. wx.showModal({
  34. title: '请先登录',
  35. showCancel: false,
  36. success: () => {
  37. wx.navigateTo({ url: '/pages/login/login' });
  38. }
  39. });
  40. return;
  41. }
  42. const searchUrl = `${app.globalData.baseUrl}/api/members/search`;
  43. console.log('[Search] URL:', searchUrl);
  44. console.log('[Search] keyword:', kw);
  45. console.log('[Search] isLoggedIn:', app.globalData.isLoggedIn);
  46. console.log('[Search] token:', app.globalData.token ? app.globalData.token.substring(0, 10) + '...' : 'null');
  47. wx.request({
  48. url: searchUrl,
  49. method: 'GET',
  50. data: { keyword: kw },
  51. header: { 'Authorization': `Bearer ${app.globalData.token}` },
  52. success: (res) => {
  53. console.log('[Search] statusCode:', res.statusCode);
  54. console.log('[Search] data:', JSON.stringify(res.data));
  55. if (res.data && res.data.success) {
  56. const list = res.data.data || [];
  57. if (list.length === 0) {
  58. wx.showToast({ title: '未找到相关成员', icon: 'none' });
  59. } else if (list.length === 1) {
  60. // 唯一结果,直接展示世系
  61. this.setData({
  62. searchResults: [],
  63. searchKeyword: list[0].name,
  64. loading: true,
  65. center: null,
  66. reversedGenerations: [],
  67. siblings: [],
  68. children: []
  69. });
  70. this.loadLineage(list[0].id);
  71. } else {
  72. this.setData({ searchResults: list });
  73. }
  74. } else {
  75. wx.showToast({ title: (res.data && res.data.message) || '搜索失败', icon: 'none' });
  76. }
  77. },
  78. fail: (err) => {
  79. console.error('[Search] 网络请求失败:', JSON.stringify(err));
  80. wx.showToast({ title: '网络失败:' + (err.errMsg || ''), icon: 'none', duration: 3000 });
  81. }
  82. });
  83. },
  84. selectMember: function (e) {
  85. const member = e.currentTarget.dataset.member;
  86. this.setData({
  87. searchResults: [],
  88. searchKeyword: member.name,
  89. loading: true,
  90. center: null,
  91. reversedGenerations: [],
  92. siblings: [],
  93. children: []
  94. });
  95. this.loadLineage(member.id);
  96. },
  97. loadLineage: function (memberId) {
  98. const lineageUrl = `${app.globalData.baseUrl}/api/lineage/${memberId}`;
  99. console.log('[Lineage] 请求URL:', lineageUrl);
  100. console.log('[Lineage] token:', app.globalData.token ? app.globalData.token.substring(0, 10) + '...' : 'null');
  101. wx.showLoading({ title: '加载中...' });
  102. wx.request({
  103. url: lineageUrl,
  104. method: 'GET',
  105. header: { 'Authorization': `Bearer ${app.globalData.token}` },
  106. success: (res) => {
  107. wx.hideLoading();
  108. console.log('[Lineage] statusCode:', res.statusCode);
  109. console.log('[Lineage] success:', res.data && res.data.success);
  110. console.log('[Lineage] data keys:', res.data && res.data.data ? Object.keys(res.data.data) : 'none');
  111. if (res.data && res.data.success) {
  112. const d = res.data.data;
  113. console.log('[Lineage] generations:', (d.generations || []).length);
  114. console.log('[Lineage] siblings:', (d.siblings || []).length);
  115. console.log('[Lineage] children:', (d.children || []).length);
  116. console.log('[Lineage] center:', JSON.stringify(d.center));
  117. const center = d.center || null;
  118. const siblings = d.siblings || [];
  119. // 计算 rpx→px 比率,用于 scroll-left
  120. const sysInfo = wx.getSystemInfoSync();
  121. const rpxRatio = sysInfo.windowWidth / 750;
  122. // 卡片宽200rpx + 间距16rpx = 216rpx/卡
  123. // padding两端各275rpx,当祖先在index=K时 scrollLeft=K*216*ratio
  124. const cardUnit = 216 * rpxRatio;
  125. // 每代:祖先 + 兄弟合并排序,计算 scrollLeft
  126. const rawGenerations = (d.generations || []).slice().reverse();
  127. const reversedGenerations = rawGenerations.map(gen => {
  128. const allPeers = [
  129. { ...gen.ancestor, isAncestor: true },
  130. ...(gen.siblings || []).map(s => ({ ...s, isAncestor: false }))
  131. ].sort((a, b) => {
  132. const oa = a.child_order || 1;
  133. const ob = b.child_order || 1;
  134. return oa !== ob ? oa - ob : a.id - b.id;
  135. });
  136. const ancIdx = allPeers.findIndex(p => p.isAncestor);
  137. return { ...gen, allPeers, scrollLeft: ancIdx * cardUnit };
  138. });
  139. // 查询人物行:center + 兄弟合并排序,计算 scrollLeft
  140. let peers = [];
  141. let peerScrollLeft = 0;
  142. if (center) {
  143. const allPeers = [
  144. { ...center, isCenter: true },
  145. ...siblings.map(s => ({ ...s, isCenter: false }))
  146. ].sort((a, b) => {
  147. const oa = a.child_order || 1;
  148. const ob = b.child_order || 1;
  149. return oa !== ob ? oa - ob : a.id - b.id;
  150. });
  151. peers = allPeers;
  152. const cIdx = allPeers.findIndex(p => p.isCenter);
  153. peerScrollLeft = cIdx * cardUnit;
  154. }
  155. // 子女行居中:将中间那个子女滚到屏幕中央
  156. const childrenArr = d.children || [];
  157. const midChildIdx = Math.floor(childrenArr.length / 2);
  158. const childrenScrollLeft = midChildIdx * cardUnit;
  159. this.setData({
  160. center,
  161. reversedGenerations,
  162. siblings,
  163. children: childrenArr,
  164. childrenScrollLeft,
  165. peers,
  166. peerScrollLeft,
  167. loading: false
  168. });
  169. } else {
  170. console.error('[Lineage] 失败:', JSON.stringify(res.data));
  171. wx.showToast({ title: (res.data && res.data.message) || '加载失败', icon: 'none' });
  172. this.setData({ loading: false });
  173. }
  174. },
  175. fail: (err) => {
  176. wx.hideLoading();
  177. console.error('[Lineage] 网络失败:', JSON.stringify(err));
  178. wx.showToast({ title: '世系加载失败:' + (err.errMsg || ''), icon: 'none', duration: 3000 });
  179. this.setData({ loading: false });
  180. }
  181. });
  182. },
  183. clearSearch: function () {
  184. this.setData({
  185. searchKeyword: '',
  186. searchResults: [],
  187. center: null,
  188. reversedGenerations: [],
  189. siblings: [],
  190. peers: [],
  191. peerScrollLeft: 0,
  192. children: [],
  193. childrenScrollLeft: 0,
  194. loading: false
  195. });
  196. },
  197. viewDetail: function (e) {
  198. const member = e.currentTarget.dataset.member;
  199. if (!member || !member.id) return;
  200. wx.request({
  201. url: `${app.globalData.baseUrl}/api/members/${member.id}`,
  202. method: 'GET',
  203. header: { 'Authorization': `Bearer ${app.globalData.token}` },
  204. success: (res) => {
  205. if (res.data && res.data.success) {
  206. this.setData({ showDetail: true, detailMember: res.data.data });
  207. }
  208. }
  209. });
  210. },
  211. switchCenter: function () {
  212. if (!this.data.detailMember) return;
  213. const m = this.data.detailMember;
  214. this.setData({
  215. showDetail: false,
  216. detailMember: null,
  217. searchKeyword: m.name,
  218. loading: true,
  219. center: null,
  220. reversedGenerations: [],
  221. siblings: [],
  222. children: []
  223. });
  224. this.loadLineage(m.id);
  225. },
  226. closeDetail: function () {
  227. this.setData({ showDetail: false, detailMember: null });
  228. },
  229. stopProp: function () {}
  230. });