App.vue 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. <script setup lang="ts">
  2. import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
  3. import { isLoggedIn } from "@/utils/auth";
  4. onLaunch((options: any) => {
  5. console.log("App Launch", options);
  6. checkLoginStatus();
  7. checkInviteCode(options);
  8. });
  9. onShow(() => {
  10. console.log("App Show");
  11. checkLoginStatus();
  12. });
  13. onHide(() => {
  14. console.log("App Hide");
  15. });
  16. const checkLoginStatus = () => {
  17. const pages = getCurrentPages();
  18. const currentPage = pages[pages.length - 1];
  19. const currentPath = currentPage ? currentPage.route : '';
  20. if (!isLoggedIn() && currentPath !== 'pages/login/login') {
  21. console.log('未登录,跳转到登录页');
  22. uni.reLaunch({
  23. url: '/pages/login/login'
  24. });
  25. }
  26. };
  27. /**
  28. * 获取当前登录用户的首页路径
  29. */
  30. const getHomePath = (): string => {
  31. const userType = uni.getStorageSync('admin_user_type') || 'admin';
  32. if (userType === 'replenisher') {
  33. return '/pages/replenish/index';
  34. }
  35. return '/pages/index/index';
  36. };
  37. /**
  38. * 检测启动参数中的邀请码和补货员绑定码
  39. * 场景:通过分享链接进入小程序时,会携带 inviteCode/bindingCode 参数
  40. */
  41. const checkInviteCode = (options: any) => {
  42. // 检查 query 参数中的邀请码
  43. if (options && options.query && options.query.inviteCode) {
  44. console.log('检测到邀请码:', options.query.inviteCode);
  45. uni.setStorageSync('pendingInviteCode', options.query.inviteCode);
  46. return;
  47. }
  48. // 检查补货员绑定码
  49. if (options && options.query && options.query.bindingCode) {
  50. console.log('检测到补货员绑定码:', options.query.bindingCode);
  51. uni.navigateTo({
  52. url: '/pages/replenish/bind?code=' + encodeURIComponent(options.query.bindingCode)
  53. });
  54. return;
  55. }
  56. // 检查 scene 场景值(小程序码场景)
  57. if (options && options.scene) {
  58. const scene = decodeURIComponent(options.scene);
  59. console.log('检测到场景值:', scene);
  60. // 尝试解码场景值,如果是绑定码格式则跳转
  61. if (/^[A-Z0-9]{24}$/.test(scene)) {
  62. console.log('场景值为补货员绑定码:', scene);
  63. uni.navigateTo({
  64. url: '/pages/replenish/bind?code=' + scene
  65. });
  66. }
  67. }
  68. };
  69. /**
  70. * 绑定邀请关系(注册成功后调用)
  71. * @param onSuccess 绑定成功回调
  72. */
  73. const bindPendingInviteRelation = async (onSuccess?: () => void) => {
  74. const inviteCode = uni.getStorageSync('pendingInviteCode');
  75. if (!inviteCode) {
  76. console.log('无待绑定的邀请码');
  77. return false;
  78. }
  79. try {
  80. const { bindInviteRelation } = await import('@/api/invite');
  81. const result = await bindInviteRelation({ inviteCode });
  82. if (result.success) {
  83. // 绑定成功,清除本地存储的邀请码
  84. uni.removeStorageSync('pendingInviteCode');
  85. uni.showToast({
  86. title: '邀请关系绑定成功',
  87. icon: 'success'
  88. });
  89. if (onSuccess) {
  90. onSuccess();
  91. }
  92. return true;
  93. } else {
  94. console.warn('绑定邀请关系返回失败:', result.message);
  95. return false;
  96. }
  97. } catch (error) {
  98. console.error('绑定邀请关系失败', error);
  99. // 即使绑定失败也清除邀请码,避免重复尝试
  100. uni.removeStorageSync('pendingInviteCode');
  101. return false;
  102. }
  103. };
  104. </script>
  105. <style>
  106. /*
  107. * 暖金黄设计系统 - 与用户端品牌统一
  108. * 特点: 纯色无渐变、简约清晰、科学排版
  109. */
  110. page {
  111. /* 背景系统 */
  112. --bg-page: #f8fafc;
  113. --bg-card: #ffffff;
  114. --bg-secondary: #f1f5f9;
  115. --bg-tertiary: #e2e8f0;
  116. background: var(--bg-page);
  117. font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
  118. font-size: 28rpx;
  119. color: #1e293b;
  120. line-height: 1.6;
  121. min-height: 100vh;
  122. }
  123. /* ==================== 色彩系统 ==================== */
  124. :root {
  125. /* 主色调 - 暖金黄 */
  126. --primary: #FFC107;
  127. --primary-light: #FFE082;
  128. --primary-dark: #FFA000;
  129. --primary-bg: #FFF8E1;
  130. /* 辅助色 - 活力橙 */
  131. --accent: #f97316;
  132. --accent-light: #fb923c;
  133. --accent-bg: #fff7ed;
  134. /* 信息色 - 天空蓝 */
  135. --info: #0ea5e9;
  136. --info-light: #38bdf8;
  137. --info-bg: #f0f9ff;
  138. /* 功能色 - 成功 */
  139. --success: #10b981;
  140. --success-bg: #ecfdf5;
  141. /* 功能色 - 警告 */
  142. --warning: #f59e0b;
  143. --warning-bg: #fffbeb;
  144. /* 功能色 - 错误 */
  145. --error: #ef4444;
  146. --error-bg: #fef2f2;
  147. /* 文字层级 */
  148. --text-primary: #1e293b;
  149. --text-secondary: #475569;
  150. --text-tertiary: #64748b;
  151. --text-muted: #94a3b8;
  152. --text-placeholder: #cbd5e1;
  153. /* 边框与分割 */
  154. --border: #e2e8f0;
  155. --border-light: #f1f5f9;
  156. --divider: #e2e8f0;
  157. /* 卡片 */
  158. --card-bg: #ffffff;
  159. --card-border: #e2e8f0;
  160. --card-radius: 16rpx;
  161. --card-radius-lg: 24rpx;
  162. --card-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.04);
  163. /* 间距系统 */
  164. --space-1: 8rpx;
  165. --space-2: 16rpx;
  166. --space-3: 24rpx;
  167. --space-4: 32rpx;
  168. --space-5: 40rpx;
  169. --space-6: 48rpx;
  170. /* 圆角系统 */
  171. --radius-xs: 4rpx;
  172. --radius-sm: 8rpx;
  173. --radius-base: 12rpx;
  174. --radius-md: 16rpx;
  175. --radius-lg: 20rpx;
  176. --radius-xl: 24rpx;
  177. --radius-full: 9999rpx;
  178. }
  179. /* ==================== 基础重置 ==================== */
  180. button {
  181. margin: 0;
  182. padding: 0;
  183. background: transparent;
  184. line-height: inherit;
  185. }
  186. button::after {
  187. border: none;
  188. }
  189. /* ==================== 卡片组件 ==================== */
  190. .card {
  191. background: var(--card-bg);
  192. border: 1rpx solid var(--card-border);
  193. border-radius: var(--card-radius);
  194. padding: 32rpx;
  195. margin: 20rpx;
  196. }
  197. /* ==================== 列表项 ==================== */
  198. .list-item {
  199. background: var(--card-bg);
  200. padding: 32rpx;
  201. border-bottom: 1rpx solid var(--border-light);
  202. display: flex;
  203. align-items: center;
  204. transition: background 0.15s;
  205. }
  206. .list-item:active {
  207. background: var(--bg-secondary);
  208. }
  209. .list-item:last-child {
  210. border-bottom: none;
  211. }
  212. /* ==================== 文字工具类 ==================== */
  213. .ellipsis {
  214. overflow: hidden;
  215. text-overflow: ellipsis;
  216. white-space: nowrap;
  217. }
  218. .ellipsis-2 {
  219. overflow: hidden;
  220. text-overflow: ellipsis;
  221. display: -webkit-box;
  222. -webkit-line-clamp: 2;
  223. -webkit-box-orient: vertical;
  224. }
  225. /* ==================== 安全区域 ==================== */
  226. .safe-area-bottom {
  227. padding-bottom: constant(safe-area-inset-bottom);
  228. padding-bottom: env(safe-area-inset-bottom);
  229. }
  230. /* ==================== 状态标签 ==================== */
  231. .status-tag {
  232. padding: 8rpx 16rpx;
  233. border-radius: 8rpx;
  234. font-size: 22rpx;
  235. font-weight: 500;
  236. display: inline-flex;
  237. align-items: center;
  238. justify-content: center;
  239. }
  240. .status-tag.success {
  241. background: var(--success-bg);
  242. color: var(--success);
  243. }
  244. .status-tag.warning {
  245. background: var(--warning-bg);
  246. color: var(--warning);
  247. }
  248. .status-tag.error {
  249. background: var(--error-bg);
  250. color: var(--error);
  251. }
  252. .status-tag.info {
  253. background: var(--info-bg);
  254. color: var(--info);
  255. }
  256. /* ==================== 输入框 ==================== */
  257. .modern-input {
  258. background: var(--bg-secondary);
  259. border: 1rpx solid var(--border);
  260. border-radius: 12rpx;
  261. padding: 24rpx 28rpx;
  262. color: var(--text-primary);
  263. font-size: 30rpx;
  264. transition: border-color 0.15s;
  265. }
  266. .modern-input:focus {
  267. border-color: var(--primary);
  268. }
  269. /* ==================== 按钮组件 ==================== */
  270. .btn-primary {
  271. background: var(--primary);
  272. color: #fff;
  273. font-weight: 600;
  274. padding: 28rpx 48rpx;
  275. border-radius: 12rpx;
  276. display: flex;
  277. align-items: center;
  278. justify-content: center;
  279. transition: opacity 0.15s;
  280. }
  281. .btn-primary:active {
  282. opacity: 0.85;
  283. }
  284. .btn-secondary {
  285. background: var(--bg-secondary);
  286. border: 1rpx solid var(--border);
  287. color: var(--text-primary);
  288. font-weight: 500;
  289. padding: 28rpx 48rpx;
  290. border-radius: 12rpx;
  291. display: flex;
  292. align-items: center;
  293. justify-content: center;
  294. transition: background 0.15s;
  295. }
  296. .btn-secondary:active {
  297. background: var(--bg-tertiary);
  298. }
  299. /* ==================== 图标容器 ==================== */
  300. .icon-box {
  301. width: 88rpx;
  302. height: 88rpx;
  303. border-radius: 20rpx;
  304. display: flex;
  305. align-items: center;
  306. justify-content: center;
  307. }
  308. .icon-box.green { background: var(--primary-bg); }
  309. .icon-box.amber { background: var(--accent-bg); }
  310. .icon-box.blue { background: var(--info-bg); }
  311. .icon-box.red { background: var(--error-bg); }
  312. .icon-box.purple { background: #faf5ff; }
  313. .icon-box.cyan { background: #ecfeff; }
  314. /* ==================== 加载动画 ==================== */
  315. @keyframes spin {
  316. to { transform: rotate(360deg); }
  317. }
  318. .loading-spinner {
  319. width: 32rpx;
  320. height: 32rpx;
  321. border: 3rpx solid var(--border);
  322. border-top-color: var(--primary);
  323. border-radius: 50%;
  324. animation: spin 1s linear infinite;
  325. }
  326. /* ==================== 分割线 ==================== */
  327. .divider {
  328. height: 1rpx;
  329. background: var(--divider);
  330. }
  331. .divider-vertical {
  332. width: 1rpx;
  333. background: var(--divider);
  334. }
  335. /* ==================== 空状态 ==================== */
  336. .empty-state {
  337. display: flex;
  338. flex-direction: column;
  339. align-items: center;
  340. justify-content: center;
  341. padding: 100rpx 0;
  342. }
  343. .empty-icon {
  344. width: 120rpx;
  345. height: 120rpx;
  346. background: var(--bg-secondary);
  347. border-radius: 24rpx;
  348. margin-bottom: 24rpx;
  349. display: flex;
  350. align-items: center;
  351. justify-content: center;
  352. }
  353. .empty-text {
  354. font-size: 28rpx;
  355. color: var(--text-muted);
  356. display: flex;
  357. align-items: center;
  358. justify-content: center;
  359. }
  360. </style>