minitest2.0 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/.editorconfig +8 -0
  2. package/.env.development +3 -0
  3. package/.env.production +3 -0
  4. package/.env.test +3 -0
  5. package/.gitattributes +1 -0
  6. package/.oxlintrc.json +10 -0
  7. package/.prettierrc.json +6 -0
  8. package/.vscode/extensions.json +9 -0
  9. package/.vscode/settings.json +13 -0
  10. package/README.md +179 -0
  11. package/auto-imports.d.ts +629 -0
  12. package/components.d.ts +21 -0
  13. package/design-qa.md +36 -0
  14. package/docs/MX_API.md +244 -0
  15. package/docs/REQUEST.md +217 -0
  16. package/docs/jsapi.js +515 -0
  17. package/docs/package-json-guide.md +132 -0
  18. package/env.d.ts +15 -0
  19. package/eslint.config.ts +26 -0
  20. package/index.html +16 -0
  21. package/package.json +83 -0
  22. package/plugins/bump-version.ts +61 -0
  23. package/postcss.config.ts +15 -0
  24. package/public/favicon.ico +0 -0
  25. package/public/images/12a73787-86a9-4891-a65f-66104746f6a8.png +0 -0
  26. package/public/images/5798d7aa-ba8b-4605-8079-58b35495ac55.png +0 -0
  27. package/public/images/73fef1e4-0fd0-4a1a-9b8b-a70a5b6acbbc.png +0 -0
  28. package/public/images/bc685b4c-0cca-4a79-924c-a8ee10e6f8eb.png +0 -0
  29. package/public/images/c3dbbd9d-be56-490e-b9f4-6ee17ebefffc.png +0 -0
  30. package/public/images/ea745a10-42aa-4f44-8d7f-3ab02cc0adcd.png +0 -0
  31. package/public/images/f5876785-b927-4347-ba19-999114240649.png +0 -0
  32. package/public/images/img.png +0 -0
  33. package/public/images/opening-reserve-estimate.png +0 -0
  34. package/public/images/position-estimate-report.png +0 -0
  35. package/src/App.vue +131 -0
  36. package/src/api/announcement.ts +405 -0
  37. package/src/api/health.ts +13 -0
  38. package/src/api/pbc-position.ts +178 -0
  39. package/src/api/rmb-position.ts +233 -0
  40. package/src/api/tam.ts +173 -0
  41. package/src/api/user.ts +92 -0
  42. package/src/auto-imports.d.ts +633 -0
  43. package/src/components/AppTitleBar.vue +376 -0
  44. package/src/components.d.ts +33 -0
  45. package/src/config/config.properties +3 -0
  46. package/src/config/env.ts +6 -0
  47. package/src/config/plugin.properties.pro +6 -0
  48. package/src/config/plugin.properties.test +6 -0
  49. package/src/core/mxApi/index.ts +451 -0
  50. package/src/core/request/index.ts +135 -0
  51. package/src/main.ts +40 -0
  52. package/src/router/index.ts +144 -0
  53. package/src/stores/app.ts +103 -0
  54. package/src/stores/counter.ts +12 -0
  55. package/src/stores/user.ts +137 -0
  56. package/src/styles/nprogress.css +22 -0
  57. package/src/styles/vant-overrides.css +42 -0
  58. package/src/types/api.d.ts +14 -0
  59. package/src/types/nprogress.d.ts +8 -0
  60. package/src/utils/auth-token.ts +241 -0
  61. package/src/utils/code-highlight.ts +165 -0
  62. package/src/utils/copy.ts +36 -0
  63. package/src/utils/query.ts +27 -0
  64. package/src/utils/request.ts +238 -0
  65. package/src/utils/rmb-forecast.ts +84 -0
  66. package/src/utils/toast.ts +61 -0
  67. package/src/views/article-detail/index.vue +289 -0
  68. package/src/views/article-edit/index.vue +600 -0
  69. package/src/views/articles/index.vue +293 -0
  70. package/src/views/clearing-detail/index.vue +26 -0
  71. package/src/views/dashboard/index.vue +18 -0
  72. package/src/views/foreign-position/index.vue +26 -0
  73. package/src/views/home/index.vue +213 -0
  74. package/src/views/login/index.vue +275 -0
  75. package/src/views/mine/index.vue +147 -0
  76. package/src/views/net-debit/index.vue +26 -0
  77. package/src/views/opening-reserve-estimate/index.vue +28 -0
  78. package/src/views/pbc-position/index.vue +357 -0
  79. package/src/views/position-estimate/index.vue +67 -0
  80. package/src/views/position-estimate-report/index.vue +28 -0
  81. package/src/views/rmb-position/index.vue +1013 -0
  82. package/src/views/rmb-position-create/index.vue +221 -0
  83. package/src/views/rmb-position-detail/index.vue +355 -0
  84. package/src/views/settings/index.vue +67 -0
  85. package/src/views/warning/index.vue +26 -0
  86. package/tsconfig.app.json +18 -0
  87. package/tsconfig.json +11 -0
  88. package/tsconfig.node.json +28 -0
  89. package/vite.config.ts +100 -0
@@ -0,0 +1,144 @@
1
+ import { createRouter, createWebHashHistory } from 'vue-router'
2
+ import { useUserStore } from '@/stores/user'
3
+
4
+ const router = createRouter({
5
+ history: createWebHashHistory(),
6
+ scrollBehavior(_to, _from, savedPosition) {
7
+ if (savedPosition) {
8
+ return savedPosition
9
+ }
10
+
11
+ return { left: 0, top: 0 }
12
+ },
13
+ routes: [
14
+ {
15
+ path: '/',
16
+ name: 'Home',
17
+ component: () => import('@/views/home/index.vue'),
18
+ meta: { requiresAuth: true, title: '头寸管理平台' },
19
+ },
20
+ {
21
+ path: '/login',
22
+ name: 'Login',
23
+ component: () => import('@/views/login/index.vue'),
24
+ meta: { hideTabbar: true, title: '账号登录' },
25
+ },
26
+ {
27
+ path: '/rmb-position',
28
+ name: 'RmbPosition',
29
+ component: () => import('@/views/rmb-position/index.vue'),
30
+ meta: { requiresAuth: true, hideTabbar: true, title: '本币预报信息查询' },
31
+ },
32
+ {
33
+ path: '/rmb-position/new',
34
+ name: 'RmbPositionCreate',
35
+ component: () => import('@/views/rmb-position-create/index.vue'),
36
+ meta: { requiresAuth: true, hideTabbar: true, title: '新增本币预报' },
37
+ },
38
+ {
39
+ path: '/rmb-position/:predRefNo',
40
+ name: 'RmbPositionDetail',
41
+ component: () => import('@/views/rmb-position-detail/index.vue'),
42
+ meta: { requiresAuth: true, hideTabbar: true, title: '预报详情' },
43
+ },
44
+ {
45
+ path: '/foreign-position',
46
+ name: 'ForeignPosition',
47
+ component: () => import('@/views/foreign-position/index.vue'),
48
+ meta: { requiresAuth: true, title: '外币头寸预报及查询' },
49
+ },
50
+ {
51
+ path: '/pbc-position',
52
+ name: 'PbcPosition',
53
+ component: () => import('@/views/pbc-position/index.vue'),
54
+ meta: { requiresAuth: true, hideTabbar: true, title: '人行头寸余额查询' },
55
+ },
56
+ {
57
+ path: '/position-estimate',
58
+ name: 'PositionEstimate',
59
+ component: () => import('@/views/position-estimate/index.vue'),
60
+ meta: { requiresAuth: true, title: '头寸匡算' },
61
+ },
62
+ {
63
+ path: '/position-estimate/report',
64
+ name: 'PositionEstimateReport',
65
+ component: () => import('@/views/position-estimate-report/index.vue'),
66
+ meta: { requiresAuth: true, hideTabbar: true, title: '头寸匡算报表' },
67
+ },
68
+ {
69
+ path: '/position-estimate/opening-reserve',
70
+ name: 'OpeningReserveEstimate',
71
+ component: () => import('@/views/opening-reserve-estimate/index.vue'),
72
+ meta: { requiresAuth: true, hideTabbar: true, title: '日初备款数匡算' },
73
+ },
74
+ {
75
+ path: '/net-debit',
76
+ name: 'NetDebit',
77
+ component: () => import('@/views/net-debit/index.vue'),
78
+ meta: { requiresAuth: true, title: '人民币净借记可用额度管理' },
79
+ },
80
+ {
81
+ path: '/clearing-detail',
82
+ name: 'ClearingDetail',
83
+ component: () => import('@/views/clearing-detail/index.vue'),
84
+ meta: { requiresAuth: true, title: '人民币小额网银清算场次明细' },
85
+ },
86
+ {
87
+ path: '/warning',
88
+ name: 'Warning',
89
+ component: () => import('@/views/warning/index.vue'),
90
+ meta: { requiresAuth: true, title: '预警信息' },
91
+ },
92
+ {
93
+ path: '/dashboard',
94
+ name: 'Dashboard',
95
+ component: () => import('@/views/dashboard/index.vue'),
96
+ meta: { requiresAuth: true, title: '看板' },
97
+ },
98
+ {
99
+ path: '/articles',
100
+ name: 'Articles',
101
+ component: () => import('@/views/articles/index.vue'),
102
+ meta: { requiresAuth: true, title: '公告' },
103
+ },
104
+ {
105
+ path: '/articles/new',
106
+ name: 'ArticlePublish',
107
+ component: () => import('@/views/article-edit/index.vue'),
108
+ meta: { requiresAuth: true, hideTabbar: true, title: '发布公告' },
109
+ },
110
+ {
111
+ path: '/articles/:id',
112
+ name: 'ArticleDetail',
113
+ component: () => import('@/views/article-detail/index.vue'),
114
+ meta: { requiresAuth: true, hideTabbar: true, title: '公告详情' },
115
+ },
116
+ {
117
+ path: '/articles/:id/edit',
118
+ name: 'ArticleEdit',
119
+ component: () => import('@/views/article-edit/index.vue'),
120
+ meta: { requiresAuth: true, hideTabbar: true, title: '编辑公告' },
121
+ },
122
+ {
123
+ path: '/mine',
124
+ name: 'Mine',
125
+ component: () => import('@/views/mine/index.vue'),
126
+ meta: { requiresAuth: true, title: '我的' },
127
+ },
128
+ {
129
+ path: '/settings',
130
+ name: 'Settings',
131
+ component: () => import('@/views/settings/index.vue'),
132
+ meta: { requiresAuth: true, hideTabbar: true, title: '设置' },
133
+ },
134
+ ],
135
+ })
136
+
137
+ router.beforeEach((to) => {
138
+ const userStore = useUserStore()
139
+ if (to.meta.requiresAuth && !userStore.token) {
140
+ return { name: 'Login' }
141
+ }
142
+ })
143
+
144
+ export default router
@@ -0,0 +1,103 @@
1
+ import { defineStore } from 'pinia'
2
+ import { waitNativeReady } from '@/core/mxApi'
3
+
4
+ /**
5
+ * 应用级状态 Store
6
+ *
7
+ * 存放全局共享的运行时状态,应用设置通过 persistedstate 持久化。
8
+ * initNativeDetection 在 main.ts 挂载前调用,组件读取时值已就绪。
9
+ */
10
+ export const useAppStore = defineStore(
11
+ 'app',
12
+ () => {
13
+ /** 是否在原生 APP WebView 环境中 */
14
+ const isNative = ref(false)
15
+
16
+ /** 是否在底部导航中显示看板入口 */
17
+ const showDashboardTab = ref(false)
18
+
19
+ /** 是否在底部导航中显示公告入口 */
20
+ const showAnnouncementTab = ref(false)
21
+
22
+ /** 后端返回的系统工作日期原始值,来自 /uaa/log/lastLoginTime 的 appDt。 */
23
+ const systemAppDate = ref('')
24
+
25
+ /** 系统工作日期,页面日期控件使用 YYYY-MM-DD。 */
26
+ const systemWorkDate = computed(() => normalizeSystemWorkDate(systemAppDate.value) || today())
27
+
28
+ /** 系统工作日期,TAM 等接口使用 YYYYMMDD。 */
29
+ const systemWorkDateCompact = computed(() => systemWorkDate.value.replaceAll('-', ''))
30
+
31
+ /**
32
+ * 初始化原生环境检测
33
+ *
34
+ * 等待敏行宿主注入完成后判断当前运行环境。
35
+ */
36
+ async function initNativeDetection() {
37
+ isNative.value = await waitNativeReady()
38
+ }
39
+
40
+ function setShowDashboardTab(value: boolean) {
41
+ showDashboardTab.value = value
42
+ }
43
+
44
+ function setShowAnnouncementTab(value: boolean) {
45
+ showAnnouncementTab.value = value
46
+ }
47
+
48
+ function setSystemAppDate(value: string) {
49
+ systemAppDate.value = value
50
+ }
51
+
52
+ function clearSystemAppDate() {
53
+ systemAppDate.value = ''
54
+ }
55
+
56
+ return {
57
+ isNative,
58
+ showDashboardTab,
59
+ showAnnouncementTab,
60
+ systemAppDate,
61
+ systemWorkDate,
62
+ systemWorkDateCompact,
63
+ initNativeDetection,
64
+ setShowDashboardTab,
65
+ setShowAnnouncementTab,
66
+ setSystemAppDate,
67
+ clearSystemAppDate,
68
+ }
69
+ },
70
+ {
71
+ persist: {
72
+ key: 'mini2_app_settings',
73
+ pick: ['showDashboardTab', 'showAnnouncementTab', 'systemAppDate'],
74
+ },
75
+ },
76
+ )
77
+
78
+ function normalizeSystemWorkDate(value: string) {
79
+ const text = value.trim()
80
+
81
+ if (!text) return ''
82
+
83
+ const dashedDate = text.match(/^(\d{4})-(\d{2})-(\d{2})/)
84
+ if (dashedDate) {
85
+ return `${dashedDate[1]}-${dashedDate[2]}-${dashedDate[3]}`
86
+ }
87
+
88
+ const compactDate = text.match(/^(\d{4})(\d{2})(\d{2})/)
89
+ if (compactDate) {
90
+ return `${compactDate[1]}-${compactDate[2]}-${compactDate[3]}`
91
+ }
92
+
93
+ return ''
94
+ }
95
+
96
+ function today() {
97
+ const now = new Date()
98
+ const year = now.getFullYear()
99
+ const month = String(now.getMonth() + 1).padStart(2, '0')
100
+ const day = String(now.getDate()).padStart(2, '0')
101
+
102
+ return `${year}-${month}-${day}`
103
+ }
@@ -0,0 +1,12 @@
1
+ import { ref, computed } from 'vue'
2
+ import { defineStore } from 'pinia'
3
+
4
+ export const useCounterStore = defineStore('counter', () => {
5
+ const count = ref(0)
6
+ const doubleCount = computed(() => count.value * 2)
7
+ function increment() {
8
+ count.value++
9
+ }
10
+
11
+ return { count, doubleCount, increment }
12
+ })
@@ -0,0 +1,137 @@
1
+ import { defineStore } from 'pinia'
2
+ import type { UserOrg } from '@/api/user'
3
+ import type { AuthTokenPayload } from '@/utils/auth-token'
4
+
5
+ const LEGACY_TOKEN_KEYS = ['token', 'refresh_token', 'user']
6
+
7
+ /** 用户登录态在本地存储中的 key,由 pinia-plugin-persistedstate 使用。 */
8
+ const AUTH_PERSIST_KEY = 'mini2_user_auth'
9
+
10
+ /**
11
+ * 用户状态 Store
12
+ *
13
+ * 使用 Composition API 风格(setup 语法)定义。
14
+ * 登录信息通过 pinia-plugin-persistedstate 保存到本地,刷新页面后可继续使用。
15
+ */
16
+ export const useUserStore = defineStore(
17
+ 'user',
18
+
19
+ () => {
20
+ // -------------------------------- State --------------------------------
21
+
22
+ /** 登录凭证,登录成功后由后端返回,后续请求通过请求头携带 */
23
+ const token = ref('1s')
24
+ const refreshToken = ref('')
25
+ const username = ref('')
26
+ const realName = ref('')
27
+ const organizationName = ref('')
28
+
29
+ /** 登录接口原始返回信息,已在保存前去除敏感字段。 */
30
+ const authPayload = ref<AuthTokenPayload | null>(null)
31
+
32
+ /** 当前登录机构,登录后取机构列表第一条。 */
33
+ const loginOrg = ref<UserOrg | null>(null)
34
+
35
+ clearLegacyStoredTokens()
36
+
37
+ // ------------------------------ Actions --------------------------------
38
+
39
+ /**
40
+ * 设置 token
41
+ * 登录成功或刷新 token 成功后调用。
42
+ */
43
+ function setToken(value: string) {
44
+ token.value = value
45
+ }
46
+
47
+ function setRefreshToken(value: string) {
48
+ refreshToken.value = value
49
+ }
50
+
51
+ function setAuthTokens(
52
+ accessToken: string,
53
+ newRefreshToken?: string,
54
+ payload?: AuthTokenPayload,
55
+ ) {
56
+ setToken(accessToken)
57
+
58
+ if (newRefreshToken) {
59
+ setRefreshToken(newRefreshToken)
60
+ }
61
+
62
+ if (payload) {
63
+ authPayload.value = payload
64
+ }
65
+ }
66
+
67
+ function setUserInfo(info: {
68
+ username?: string
69
+ realName?: string
70
+ organizationName?: string
71
+ }) {
72
+ username.value = info.username || username.value
73
+ realName.value = info.realName || realName.value
74
+ organizationName.value = info.organizationName || organizationName.value
75
+ }
76
+
77
+ function setAuthPayload(payload: AuthTokenPayload | null) {
78
+ authPayload.value = payload
79
+ }
80
+
81
+ function setLoginOrg(org: UserOrg) {
82
+ loginOrg.value = org
83
+ organizationName.value = org.orgCnAbre || organizationName.value
84
+ }
85
+
86
+ /**
87
+ * 清空 token
88
+ * 退出登录或刷新 token 失败时调用。
89
+ */
90
+ function clearToken() {
91
+ token.value = ''
92
+ refreshToken.value = ''
93
+ username.value = ''
94
+ realName.value = ''
95
+ organizationName.value = ''
96
+ authPayload.value = null
97
+ loginOrg.value = null
98
+ }
99
+
100
+ // ----------------------------- Return ----------------------------------
101
+
102
+ return {
103
+ token,
104
+ refreshToken,
105
+ username,
106
+ realName,
107
+ organizationName,
108
+ authPayload,
109
+ loginOrg,
110
+ setToken,
111
+ setRefreshToken,
112
+ setAuthTokens,
113
+ setUserInfo,
114
+ setAuthPayload,
115
+ setLoginOrg,
116
+ clearToken,
117
+ }
118
+ },
119
+ {
120
+ persist: {
121
+ key: AUTH_PERSIST_KEY,
122
+ pick: [
123
+ 'token',
124
+ 'refreshToken',
125
+ 'username',
126
+ 'realName',
127
+ 'organizationName',
128
+ 'authPayload',
129
+ 'loginOrg',
130
+ ],
131
+ },
132
+ },
133
+ )
134
+
135
+ function clearLegacyStoredTokens() {
136
+ LEGACY_TOKEN_KEYS.forEach((key) => localStorage.removeItem(key))
137
+ }
@@ -0,0 +1,22 @@
1
+ #nprogress {
2
+ pointer-events: none;
3
+ }
4
+
5
+ #nprogress .bar {
6
+ top: calc(50px + env(safe-area-inset-top));
7
+ left: max(0px, calc((100vw - 750px) / 2));
8
+ width: min(100vw, 750px);
9
+ height: 2px;
10
+ background: #f22f3d;
11
+ z-index: 21;
12
+ }
13
+
14
+ #nprogress .peg {
15
+ box-shadow:
16
+ 0 0 10px #f22f3d,
17
+ 0 0 5px #f22f3d;
18
+ }
19
+
20
+ #nprogress .spinner {
21
+ display: none;
22
+ }
@@ -0,0 +1,42 @@
1
+ :root {
2
+ --van-toast-max-width: min(78vw, 360px);
3
+ --van-toast-font-size: 14px;
4
+ --van-toast-line-height: 1.45;
5
+ --van-toast-text-color: #fff;
6
+ --van-toast-radius: 8px;
7
+ --van-toast-text-min-width: 96px;
8
+ --van-toast-text-padding: 9px 14px;
9
+ --van-toast-default-padding: 14px 16px;
10
+ --van-toast-default-width: auto;
11
+ --van-toast-default-min-height: 0;
12
+ --van-toast-background: rgba(17, 24, 39, 0.82);
13
+ --van-toast-loading-icon-color: #fff;
14
+ }
15
+
16
+ .van-toast {
17
+ color: #fff !important;
18
+ background: rgba(17, 24, 39, 0.82) !important;
19
+ box-shadow: 0 10px 28px rgba(15, 23, 42, 0.2);
20
+ backdrop-filter: blur(8px);
21
+ -webkit-backdrop-filter: blur(8px);
22
+ }
23
+
24
+ .van-toast--text {
25
+ width: fit-content;
26
+ min-width: 96px;
27
+ max-width: min(78vw, 360px);
28
+ min-height: 0 !important;
29
+ padding: 9px 14px !important;
30
+ border-radius: 8px !important;
31
+ }
32
+
33
+ .van-toast__text {
34
+ color: #fff !important;
35
+ font-size: 14px;
36
+ line-height: 1.45;
37
+ word-break: break-word;
38
+ }
39
+
40
+ .van-toast__loading {
41
+ color: #fff !important;
42
+ }
@@ -0,0 +1,14 @@
1
+ /** 后端统一响应格式 */
2
+ export interface ApiResponse<T = unknown> {
3
+ code: number | string
4
+ message: string
5
+ data: T
6
+ }
7
+
8
+ /** 分页响应 */
9
+ export interface PaginatedData<T> {
10
+ list: T[]
11
+ total: number
12
+ page: number
13
+ pageSize: number
14
+ }
@@ -0,0 +1,8 @@
1
+ declare module 'nprogress' {
2
+ const NProgress: {
3
+ configure(options: { showSpinner?: boolean }): void
4
+ start(): void
5
+ done(): void
6
+ }
7
+ export default NProgress
8
+ }