business_tms_program 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 (136) hide show
  1. package/.editorconfig +12 -0
  2. package/.eslintrc-auto-import.json +113 -0
  3. package/.eslintrc.js +121 -0
  4. package/.prettierrc.js +9 -0
  5. package/.stylelintignore +4 -0
  6. package/README.md +43 -0
  7. package/components.d.ts +23 -0
  8. package/index.html +20 -0
  9. package/package.json +70 -0
  10. package/shims-uni.d.ts +10 -0
  11. package/src/App.vue +81 -0
  12. package/src/api/afterSale.ts +184 -0
  13. package/src/api/context.ts +26 -0
  14. package/src/api/device.ts +134 -0
  15. package/src/api/index.ts +80 -0
  16. package/src/api/installtion.ts +155 -0
  17. package/src/api/model/index.ts +15 -0
  18. package/src/api/model/userModel.ts +62 -0
  19. package/src/api/order.ts +49 -0
  20. package/src/api/system.ts +19 -0
  21. package/src/api/user.ts +171 -0
  22. package/src/auto-imports.d.ts +108 -0
  23. package/src/components/ConfirmDialog.vue +101 -0
  24. package/src/components/DaySelect.vue +212 -0
  25. package/src/components/Drawer.vue +104 -0
  26. package/src/components/DrawerSelect.vue +105 -0
  27. package/src/components/DropMenu.vue +144 -0
  28. package/src/components/Empty.vue +49 -0
  29. package/src/components/Loading.vue +41 -0
  30. package/src/components/RippleBtn.vue +159 -0
  31. package/src/components/SinglePick.vue +120 -0
  32. package/src/components/Skeleton.vue +43 -0
  33. package/src/components/Timeline.vue +85 -0
  34. package/src/components/Upload.vue +217 -0
  35. package/src/config/app.ts +32 -0
  36. package/src/config/env.ts +29 -0
  37. package/src/dict/afterSale.ts +161 -0
  38. package/src/dict/device.ts +29 -0
  39. package/src/dict/installtion.ts +141 -0
  40. package/src/dict/systems.ts +4 -0
  41. package/src/env.d.ts +8 -0
  42. package/src/hooks/useForm.ts +222 -0
  43. package/src/hooks/useUpload.ts +80 -0
  44. package/src/main.ts +8 -0
  45. package/src/manifest.json +39 -0
  46. package/src/pages/acceptance/DeviceInfo.vue +132 -0
  47. package/src/pages/acceptance/list.vue +276 -0
  48. package/src/pages/afterSale/DeviceInfo.vue +128 -0
  49. package/src/pages/afterSale/Step.vue +0 -0
  50. package/src/pages/afterSale/faultReport.vue +552 -0
  51. package/src/pages/afterSale/orderDetail.vue +327 -0
  52. package/src/pages/afterSale/orderFinish.vue +517 -0
  53. package/src/pages/afterSale/orderList.vue +305 -0
  54. package/src/pages/afterSale/returnVisit.vue +288 -0
  55. package/src/pages/afterSale/searchDeviceList.vue +148 -0
  56. package/src/pages/device/Search.vue +201 -0
  57. package/src/pages/device/acceptance.vue +270 -0
  58. package/src/pages/device/detail.vue +165 -0
  59. package/src/pages/device/index.vue +322 -0
  60. package/src/pages/device/info.vue +140 -0
  61. package/src/pages/device/list.vue +219 -0
  62. package/src/pages/device/materialTowerCode.vue +589 -0
  63. package/src/pages/device/searchList.vue +224 -0
  64. package/src/pages/installtion/Record.vue +145 -0
  65. package/src/pages/installtion/StatusTimeline.vue +85 -0
  66. package/src/pages/installtion/addAcceptance.vue +409 -0
  67. package/src/pages/installtion/addRecord.vue +338 -0
  68. package/src/pages/installtion/orderDetail.vue +220 -0
  69. package/src/pages/installtion/orderList.vue +100 -0
  70. package/src/pages/user/component/PersonAgree.vue +226 -0
  71. package/src/pages/user/component/PrivayAgree.vue +221 -0
  72. package/src/pages/user/component/SliderCode.vue +173 -0
  73. package/src/pages/user/forgetPassword.vue +249 -0
  74. package/src/pages/user/index.vue +139 -0
  75. package/src/pages/user/login.vue +342 -0
  76. package/src/pages/user/register.vue +348 -0
  77. package/src/pages/user/repassword.vue +329 -0
  78. package/src/pages/user/utils/mcaptcha.js +75 -0
  79. package/src/pages/user/utils/verifyCode.ts +41 -0
  80. package/src/pages/workspace/index.vue +225 -0
  81. package/src/pages.json +203 -0
  82. package/src/shime-uni.d.ts +6 -0
  83. package/src/static/icon/system/breeder_icon.png +0 -0
  84. package/src/static/icon/system/check.png +0 -0
  85. package/src/static/icon/system/factory_icon.png +0 -0
  86. package/src/static/icon/system/plus.png +0 -0
  87. package/src/static/icon/system/right.png +0 -0
  88. package/src/static/icon/system/unCheck.png +0 -0
  89. package/src/static/icon/tab/search.png +0 -0
  90. package/src/static/icon/tab/user.png +0 -0
  91. package/src/static/icon/tab/user_active.png +0 -0
  92. package/src/static/icon/tab/workspace.png +0 -0
  93. package/src/static/icon/tab/workspace_active.png +0 -0
  94. package/src/static/img/active_dot.png +0 -0
  95. package/src/static/img/afterSale_icon.png +0 -0
  96. package/src/static/img/check.png +0 -0
  97. package/src/static/img/close.png +0 -0
  98. package/src/static/img/confirm.png +0 -0
  99. package/src/static/img/empty.png +0 -0
  100. package/src/static/img/equipment_icon.png +0 -0
  101. package/src/static/img/fault_icon.png +0 -0
  102. package/src/static/img/install_icon.png +0 -0
  103. package/src/static/img/login_bg2.png +0 -0
  104. package/src/static/img/movable_right.png +0 -0
  105. package/src/static/img/navigation.png +0 -0
  106. package/src/static/img/psw_off.png +0 -0
  107. package/src/static/img/psw_on.png +0 -0
  108. package/src/static/img/scan.png +0 -0
  109. package/src/static/img/scan_icon.png +0 -0
  110. package/src/static/img/search.png +0 -0
  111. package/src/static/img/turn_right.png +0 -0
  112. package/src/static/img/unActive_dot.png +0 -0
  113. package/src/static/img/verifyBg.png +0 -0
  114. package/src/stores/index.ts +11 -0
  115. package/src/stores/modules/customer.ts +146 -0
  116. package/src/stores/modules/installtion.ts +30 -0
  117. package/src/stores/modules/system.ts +56 -0
  118. package/src/stores/modules/user.ts +133 -0
  119. package/src/stores/types.ts +16 -0
  120. package/src/stores/utils.ts +6 -0
  121. package/src/styles/index.less +63 -0
  122. package/src/types/chengyiApi.d.ts +36 -0
  123. package/src/types/index.d.ts +95 -0
  124. package/src/utils/address.ts +17 -0
  125. package/src/utils/cipher.ts +61 -0
  126. package/src/utils/form.ts +155 -0
  127. package/src/utils/httpEnum.ts +31 -0
  128. package/src/utils/image.ts +21 -0
  129. package/src/utils/index.ts +111 -0
  130. package/src/utils/request.ts +139 -0
  131. package/src/utils/requestCancelHandle.ts +67 -0
  132. package/stylelint.config.js +87 -0
  133. package/tsconfig.docs.json +11 -0
  134. package/tsconfig.json +30 -0
  135. package/typedoc.json +6 -0
  136. package/vite.config.ts +55 -0
@@ -0,0 +1,171 @@
1
+
2
+ import context from '@/api/context'
3
+ import type {
4
+ CaptchaParams,
5
+ GetCaptchaModel,
6
+ LoginParams
7
+ } from './model/userModel';
8
+ import http from '@/utils/request';
9
+ import { APPID } from '@/config/app';
10
+ const userApi = context.basicApi + '/work/account';
11
+
12
+ enum Api {
13
+ Login = '/csf-auth/token',
14
+ Logout = '/csf-auth/token/logout',
15
+ GetCaptcha = '/csf-auth/captcha'
16
+ }
17
+
18
+ // 获取验证码
19
+ export function getCaptcha() {
20
+ return http.get<GetCaptchaModel>({
21
+ url: Api.GetCaptcha
22
+ });
23
+ }
24
+
25
+ // 登录
26
+ export function loginApi(data: LoginParams, captcha?: CaptchaParams) {
27
+ return http.post(
28
+ {
29
+ data,
30
+ url: Api.Login,
31
+ header: {
32
+ 'Captcha-Key': captcha?.key,
33
+ 'Captcha-Code': captcha?.code
34
+ }
35
+ },
36
+ { joinParamsToUrl: true }
37
+ );
38
+ }
39
+
40
+ // 登出
41
+ export function doLogout() {
42
+ return http.get({
43
+ url: Api.Logout
44
+ });
45
+ }
46
+
47
+ export interface registerParams {
48
+ "nickname": string,
49
+ "mobile": number | undefined,
50
+ "code": number | undefined,
51
+ "password": string,
52
+ "confirmPassword": string
53
+ }
54
+
55
+
56
+ // 注册
57
+ export function registerApi(data: registerParams): Promise<{id: number}> {
58
+ return http.post({
59
+ url: `${userApi}/register`,
60
+ data: { ...data } // 展开参数对象
61
+ });
62
+ }
63
+
64
+ export interface resetPasswordParams {
65
+ "mobile": number | undefined,
66
+ "code": number | undefined,
67
+ "password": string,
68
+ "confirmPassword": string
69
+ }
70
+
71
+ export function resetPassword(data: resetPasswordParams): Promise<boolean> {
72
+ return http.post({
73
+ url: `${userApi}/resetPassword`,
74
+ data: { ...data } // 展开参数对象
75
+ });
76
+ }
77
+
78
+ export interface accountLoginParams {
79
+ "account": string | number,
80
+ "password": string
81
+ }
82
+
83
+ export interface accountLoginResponse {
84
+ "token": string,
85
+ "appId": string,
86
+ menuList: any[],
87
+ permsList: any[]
88
+ }
89
+ export function accountLogin(data: accountLoginParams): Promise<accountLoginResponse> {
90
+ return http.post({
91
+ url: `${context.loginApi}/user/login`,
92
+ data: { ...data } // 展开参数对象
93
+ });
94
+ }
95
+
96
+
97
+ export function getAccountList(data: object): Promise<any> {
98
+ return http.post({
99
+ url: `${userApi}/queryPageList`,
100
+ data: { ...data } // 展开参数对象
101
+ }, {
102
+ permActionName: 'getAccountList'
103
+ });
104
+ }
105
+
106
+ export type updatePswdParams = {
107
+ mobile: number | undefined | string;
108
+ oldPassword: string;
109
+ newPassword: string;
110
+ };
111
+ export function updatePswd(data: updatePswdParams): Promise<any> {
112
+ return http.post({
113
+ url: `${userApi}/updatePassword`,
114
+ data: {...data } // 展开参数对象
115
+ }, {
116
+ permActionName: 'updatePassword'
117
+ });
118
+ }
119
+
120
+ type RouteReq = {
121
+ appId?: string;
122
+ menuList?: any[];
123
+ permsList?: string[];
124
+ token?: string;
125
+ [property: string]: any;
126
+ };
127
+ export function getLoginInfo(data = {
128
+ appId: APPID,
129
+ current: 1,
130
+ size: 1000
131
+ }): Promise<RouteReq> {
132
+ return http.post({
133
+ url: `${context.loginApi}/user/login/info`,
134
+ data: { ...data } // 展开参数对象
135
+ }, {
136
+ permActionName: 'user:login:getLoginInfo'
137
+ });
138
+ }
139
+
140
+ export function getUserInfoApi(data = {
141
+ desensitize: false
142
+ }): Promise<RouteReq> {
143
+ return http.post({
144
+ url: `${context.loginApi}/user/info`,
145
+ data: { ...data } // 展开参数对象
146
+ }, {
147
+ permActionName: 'getUserInfo'
148
+ });
149
+ }
150
+
151
+ export function getResetPswAuth(data: { mobile: number | undefined; code: string }): Promise<boolean> {
152
+ return http.post({
153
+ url: `${context.loginApi}/work/account/checkResetCode`,
154
+ data: { ...data } // 展开参数对象
155
+ });
156
+ }
157
+ export function getRegisterPswAuth(data: { mobile: number | undefined; code: string }): Promise<boolean> {
158
+ return http.post({
159
+ url: `${context.loginApi}/work/account/checkRegisterCode`,
160
+ data: { ...data } // 展开参数对象
161
+ });
162
+ }
163
+ // /user/getUserList 获取用户列表
164
+ export function getUserListApi(data: object): Promise<any> {
165
+ return http.post({
166
+ url: `${context.loginApi}/user/getUserList`,
167
+ data: {...data } // 展开参数对象
168
+ }, {
169
+ permActionName: 'getUserList'
170
+ });
171
+ }
@@ -0,0 +1,108 @@
1
+ /* eslint-disable */
2
+ /* prettier-ignore */
3
+ // @ts-nocheck
4
+ // noinspection JSUnusedGlobalSymbols
5
+ // Generated by unplugin-auto-import
6
+ // biome-ignore lint: disable
7
+ export {}
8
+ declare global {
9
+ const EffectScope: typeof import('vue')['EffectScope']
10
+ const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
11
+ const computed: typeof import('vue')['computed']
12
+ const createApp: typeof import('vue')['createApp']
13
+ const createPinia: typeof import('pinia')['createPinia']
14
+ const customRef: typeof import('vue')['customRef']
15
+ const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
16
+ const defineComponent: typeof import('vue')['defineComponent']
17
+ const defineStore: typeof import('pinia')['defineStore']
18
+ const effectScope: typeof import('vue')['effectScope']
19
+ const getActivePinia: typeof import('pinia')['getActivePinia']
20
+ const getCurrentInstance: typeof import('vue')['getCurrentInstance']
21
+ const getCurrentScope: typeof import('vue')['getCurrentScope']
22
+ const h: typeof import('vue')['h']
23
+ const inject: typeof import('vue')['inject']
24
+ const isProxy: typeof import('vue')['isProxy']
25
+ const isReactive: typeof import('vue')['isReactive']
26
+ const isReadonly: typeof import('vue')['isReadonly']
27
+ const isRef: typeof import('vue')['isRef']
28
+ const mapActions: typeof import('pinia')['mapActions']
29
+ const mapGetters: typeof import('pinia')['mapGetters']
30
+ const mapState: typeof import('pinia')['mapState']
31
+ const mapStores: typeof import('pinia')['mapStores']
32
+ const mapWritableState: typeof import('pinia')['mapWritableState']
33
+ const markRaw: typeof import('vue')['markRaw']
34
+ const nextTick: typeof import('vue')['nextTick']
35
+ const onActivated: typeof import('vue')['onActivated']
36
+ const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
37
+ const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress']
38
+ const onBeforeMount: typeof import('vue')['onBeforeMount']
39
+ const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
40
+ const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
41
+ const onDeactivated: typeof import('vue')['onDeactivated']
42
+ const onError: typeof import('@dcloudio/uni-app')['onError']
43
+ const onErrorCaptured: typeof import('vue')['onErrorCaptured']
44
+ const onHide: typeof import('@dcloudio/uni-app')['onHide']
45
+ const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch']
46
+ const onLoad: typeof import('@dcloudio/uni-app')['onLoad']
47
+ const onMounted: typeof import('vue')['onMounted']
48
+ const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']
49
+ const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']
50
+ const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']
51
+ const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']
52
+ const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']
53
+ const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound']
54
+ const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll']
55
+ const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh']
56
+ const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom']
57
+ const onReady: typeof import('@dcloudio/uni-app')['onReady']
58
+ const onRenderTracked: typeof import('vue')['onRenderTracked']
59
+ const onRenderTriggered: typeof import('vue')['onRenderTriggered']
60
+ const onResize: typeof import('@dcloudio/uni-app')['onResize']
61
+ const onScopeDispose: typeof import('vue')['onScopeDispose']
62
+ const onServerPrefetch: typeof import('vue')['onServerPrefetch']
63
+ const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage']
64
+ const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline']
65
+ const onShow: typeof import('@dcloudio/uni-app')['onShow']
66
+ const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap']
67
+ const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange']
68
+ const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection']
69
+ const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
70
+ const onUnmounted: typeof import('vue')['onUnmounted']
71
+ const onUpdated: typeof import('vue')['onUpdated']
72
+ const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
73
+ const provide: typeof import('vue')['provide']
74
+ const reactive: typeof import('vue')['reactive']
75
+ const readonly: typeof import('vue')['readonly']
76
+ const ref: typeof import('vue')['ref']
77
+ const resolveComponent: typeof import('vue')['resolveComponent']
78
+ const setActivePinia: typeof import('pinia')['setActivePinia']
79
+ const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
80
+ const shallowReactive: typeof import('vue')['shallowReactive']
81
+ const shallowReadonly: typeof import('vue')['shallowReadonly']
82
+ const shallowRef: typeof import('vue')['shallowRef']
83
+ const storeToRefs: typeof import('pinia')['storeToRefs']
84
+ const toRaw: typeof import('vue')['toRaw']
85
+ const toRef: typeof import('vue')['toRef']
86
+ const toRefs: typeof import('vue')['toRefs']
87
+ const toValue: typeof import('vue')['toValue']
88
+ const triggerRef: typeof import('vue')['triggerRef']
89
+ const unref: typeof import('vue')['unref']
90
+ const useAttrs: typeof import('vue')['useAttrs']
91
+ const useCssModule: typeof import('vue')['useCssModule']
92
+ const useCssVars: typeof import('vue')['useCssVars']
93
+ const useId: typeof import('vue')['useId']
94
+ const useModel: typeof import('vue')['useModel']
95
+ const useSlots: typeof import('vue')['useSlots']
96
+ const useStore: typeof import('@/helper/pinia-auto-refs')['useStore']
97
+ const useTemplateRef: typeof import('vue')['useTemplateRef']
98
+ const watch: typeof import('vue')['watch']
99
+ const watchEffect: typeof import('vue')['watchEffect']
100
+ const watchPostEffect: typeof import('vue')['watchPostEffect']
101
+ const watchSyncEffect: typeof import('vue')['watchSyncEffect']
102
+ }
103
+ // for type re-export
104
+ declare global {
105
+ // @ts-ignore
106
+ export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
107
+ import('vue')
108
+ }
@@ -0,0 +1,101 @@
1
+ <template>
2
+ <view v-if="show" class="dialog-mask">
3
+ <view class="dialog-container">
4
+ <view class="dialog-header">{{ title }}</view>
5
+ <view class="dialog-content">
6
+ <slot></slot>
7
+ </view>
8
+ <view class="dialog-footer">
9
+ <button class="btn cancel" size="mini" @tap="handleCancel">{{ cancelText }}</button>
10
+ <button class="btn confirm" size="mini" @tap="handleConfirm">{{ confirmText }}</button>
11
+ </view>
12
+ </view>
13
+ </view>
14
+ </template>
15
+
16
+ <script>
17
+ export default {
18
+ props: {
19
+ show: Boolean,
20
+ title: {
21
+ type: String,
22
+ default: '提示'
23
+ },
24
+ cancelText: {
25
+ type: String,
26
+ default: '取消'
27
+ },
28
+ confirmText: {
29
+ type: String,
30
+ default: '确认'
31
+ }
32
+ },
33
+ methods: {
34
+ handleCancel() {
35
+ this.$emit('cancel');
36
+ this.$emit('update:show', false);
37
+ },
38
+ handleConfirm() {
39
+ this.$emit('confirm');
40
+ this.$emit('update:show', false);
41
+ }
42
+ }
43
+ };
44
+ </script>
45
+
46
+ <style lang="scss" scoped>
47
+ .dialog-mask {
48
+ position: fixed;
49
+ top: 0;
50
+ left: 0;
51
+ right: 0;
52
+ bottom: 0;
53
+ background: rgba(0, 0, 0, 0.5);
54
+ display: flex;
55
+ justify-content: center;
56
+ align-items: center;
57
+ z-index: 999;
58
+ }
59
+
60
+ .dialog-container {
61
+ width: 80%;
62
+ background: #fff;
63
+ border-radius: 12rpx;
64
+ padding: 30rpx;
65
+ }
66
+
67
+ .dialog-header {
68
+ font-size: 34rpx;
69
+ font-weight: bold;
70
+ text-align: center;
71
+ margin-bottom: 20rpx;
72
+ }
73
+
74
+ .dialog-content {
75
+ padding: 20rpx 0;
76
+ font-size: 28rpx;
77
+ }
78
+
79
+ .dialog-footer {
80
+ display: flex;
81
+ justify-content: space-between;
82
+ margin-top: 30rpx;
83
+
84
+ .btn {
85
+ flex: 1;
86
+ margin: 0 10rpx;
87
+ border-radius: 8rpx;
88
+ &::after { border: none }
89
+
90
+ &.cancel {
91
+ background: #f0f0f0;
92
+ color: #333;
93
+ }
94
+
95
+ &.confirm {
96
+ background: #007aff;
97
+ color: #fff;
98
+ }
99
+ }
100
+ }
101
+ </style>
@@ -0,0 +1,212 @@
1
+ <script setup lang="ts">
2
+ import { ref, reactive, onMounted } from "vue";
3
+ const firstIndex = 10
4
+ const currentWeek = ref<Date[]>([]);
5
+ const selectedDate = ref<Date>(new Date());
6
+ const selectIndex = ref<number>(new Date().getDay());
7
+ const swiperItems = ref<Date[][]>([]);
8
+ const currentIndex = ref(firstIndex);
9
+ const monthDate = ref(new Date().toISOString().substring(0, 7));
10
+ // 生成一周日期
11
+ const generateWeekDates = (date: Date) => {
12
+ const week = [];
13
+ const day = date.getDay(); // 0是周日,1是周一,...6是周六
14
+ const sunday = new Date(date);
15
+ sunday.setDate(date.getDate() - day); // 计算本周日的日期
16
+ // 从周日开始,依次添加周日到周六
17
+ for (let i = 0; i < 7; i++) {
18
+ const d = new Date(sunday);
19
+ d.setDate(sunday.getDate() + i);
20
+ week.push(d);
21
+ }
22
+ return week;
23
+ };
24
+
25
+ // 初始化数据
26
+ const initSwiperItems = (date?: Date) => {
27
+ const items = [];
28
+ // 初始加载前后各10周
29
+ if (date) {
30
+ for (let i = -firstIndex; i <= firstIndex; i++) {
31
+ const baseDate = new Date(date);
32
+ baseDate.setDate(baseDate.getDate() + i * 7);
33
+ items.push(generateWeekDates(baseDate));
34
+ }
35
+ } else {
36
+ for (let i = -firstIndex; i <= firstIndex; i++) {
37
+ const baseDate = date || new Date();
38
+ baseDate.setDate(baseDate.getDate() + i * 7);
39
+ items.push(generateWeekDates(baseDate));
40
+ }
41
+ }
42
+ swiperItems.value = items;
43
+ currentWeek.value = items[firstIndex];
44
+ currentIndex.value = firstIndex;
45
+ selectedDate.value = items[firstIndex][selectIndex.value];
46
+ };
47
+
48
+ // 动态加载更多日期范围
49
+ const loadMoreItems = (direction: number, current: number) => {
50
+ const newItems = [...swiperItems.value];
51
+ for (let i = 0; i <= 9; i++) {
52
+ const offset = i * direction;
53
+ if (direction > 0) {
54
+ // 向右堆
55
+ const baseDate = new Date(swiperItems.value[swiperItems.value.length - 1][0]);
56
+ baseDate.setDate(baseDate.getDate() + (offset + 1) * 7);
57
+ newItems.push(generateWeekDates(baseDate));
58
+ } else {
59
+ // 向左堆
60
+ const baseDate = new Date(swiperItems.value[0][0]);
61
+ baseDate.setDate(baseDate.getDate() + (offset - 1) * 7);
62
+ newItems.unshift(generateWeekDates(baseDate));
63
+ }
64
+ }
65
+ swiperItems.value = newItems;
66
+ if (direction < 0) {
67
+ currentWeek.value = swiperItems.value[current + 10];
68
+ currentIndex.value = current + 10;
69
+ } else {
70
+ currentWeek.value = swiperItems.value[current];
71
+ }
72
+ };
73
+
74
+ // 切换周
75
+ const changeWeek = (e: any) => {
76
+ const { current, source } = e.detail;
77
+ if (source !== 'touch') return;
78
+ const direction = getPositionRelativeToMiddle(swiperItems.value, current);
79
+ currentWeek.value = swiperItems.value[current];
80
+ const selectDate = swiperItems.value[current][selectIndex.value]
81
+ selectedDate.value = selectDate;
82
+ // 更新月份
83
+ monthDate.value = selectDate?.toISOString().substring(0, 7);
84
+ if (direction !== 0) {
85
+ // 向左滑动时加载更多
86
+ const leftFlag = current < 3;
87
+ const rightFlag = current > swiperItems.value.length - 3;
88
+ if (leftFlag || rightFlag) {
89
+ loadMoreItems(direction, current);
90
+ return;
91
+ }
92
+ }
93
+ };
94
+
95
+ function getPositionRelativeToMiddle(arr: any, current: number): number {
96
+ const middleIndex = Math.floor(arr.length / 2);
97
+ if (current === middleIndex) {
98
+ return 0;
99
+ } else if (current < middleIndex) {
100
+ return -1;
101
+ } else {
102
+ return 1;
103
+ }
104
+ }
105
+
106
+ // 初始化
107
+ onMounted(() => {
108
+ initSwiperItems();
109
+ });
110
+
111
+ const bindMonthDateChange = (e: any) => {
112
+ const { value } = e.detail;
113
+ monthDate.value = value;
114
+ swiperItems.value = [];
115
+ currentWeek.value = [];
116
+ const [year, month] = value.split('-');
117
+ const date = new Date(Date.UTC(year, month-1, 1));
118
+ nextTick(() => {
119
+ initSwiperItems(date);
120
+ })
121
+ }
122
+ const handleSelectDate = (date: Date, index: number) => {
123
+ selectedDate.value = date;
124
+ selectIndex.value = index;
125
+ // 更新月份
126
+ monthDate.value = date.toISOString().substring(0, 7);
127
+ }
128
+ const formatDay = (date: Date) => {
129
+ if (date.getDate() === 1) {
130
+ return date.getMonth() + 1 + '月'
131
+ }
132
+ return date.getDate()
133
+ }
134
+ </script>
135
+
136
+ <template>
137
+ <view class="page">
138
+ <view class="flex">
139
+ <view>工作日历</view>
140
+ <picker class="picker-date-month--test" mode="date" fields="month"
141
+ :value="monthDate" @change="bindMonthDateChange">
142
+ <view class="uni-input">{{monthDate}}</view>
143
+ </picker>
144
+ </view>
145
+ <swiper
146
+ :current="currentIndex"
147
+ @change="changeWeek"
148
+ class="week-swiper"
149
+ >
150
+ <swiper-item v-for="(week, index) in swiperItems" :key="index">
151
+ <view class="week-container">
152
+ <view
153
+ v-for="(date, i) in week"
154
+ :key="i"
155
+ class="day-item"
156
+ @click="handleSelectDate(date, i)"
157
+ >
158
+ <text class="day-name">{{ ['日','一','二','三','四','五','六'][date.getDay()] }}</text>
159
+ <text class="day-date">{{ formatDay(date) }}</text>
160
+ <view
161
+ v-if="date.toDateString() === selectedDate.toDateString()"
162
+ class="selected-indicator"
163
+ />
164
+ </view>
165
+ </view>
166
+ </swiper-item>
167
+ </swiper>
168
+ </view>
169
+ </template>
170
+
171
+ <style scoped>
172
+ .flex {
173
+ display: flex;
174
+ align-items: center;
175
+ }
176
+ .week-swiper {
177
+ height: 100px;
178
+ }
179
+
180
+ .week-container {
181
+ display: flex;
182
+ justify-content: space-around;
183
+ padding: 10px 0;
184
+ }
185
+
186
+ .day-item {
187
+ display: flex;
188
+ flex-direction: column;
189
+ align-items: center;
190
+ padding: 8px;
191
+ position: relative;
192
+ }
193
+
194
+ .day-name {
195
+ font-size: 12px;
196
+ color: #666;
197
+ margin-bottom: 4px;
198
+ }
199
+
200
+ .day-date {
201
+ font-size: 16px;
202
+ }
203
+
204
+ .selected-indicator {
205
+ position: absolute;
206
+ bottom: 0;
207
+ width: 6px;
208
+ height: 6px;
209
+ border-radius: 50%;
210
+ background-color: #007aff;
211
+ }
212
+ </style>
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <view class="drawer-container" v-if="showMask" @touchmove.prevent.stop="noop" >
3
+ <!-- 遮罩层 -->
4
+ <view
5
+ class="mask"
6
+ :class="{ 'mask-show': showDrawer }"
7
+ ></view>
8
+
9
+ <!-- 抽屉内容 -->
10
+ <view
11
+ class="drawer-content"
12
+ :class="{ 'drawer-up': showDrawer }"
13
+ :style="{ height: height + '%' }"
14
+ >
15
+ <slot></slot>
16
+ <view v-if="drawType === 'default'" class="close-btn"
17
+ @tap="closeDrawer">×</view>
18
+ </view>
19
+ </view>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ const props = defineProps({
24
+ // 控制抽屉显示
25
+ modelValue: {
26
+ type: Boolean,
27
+ default: false
28
+ },
29
+ // 抽屉高度百分比
30
+ height: {
31
+ type: Number,
32
+ default: 60
33
+ },
34
+ drawType: {
35
+ type: String,
36
+ default: 'default'
37
+ }
38
+ });
39
+
40
+ const emit = defineEmits(['update:modelValue']);
41
+ const showDrawer = ref(false);
42
+ const showMask = ref(false);
43
+
44
+ watch(() => props.modelValue, (val) => {
45
+ if (val) {
46
+ showMask.value = true;
47
+ setTimeout(() => showDrawer.value = true, 50);
48
+ } else {
49
+ showDrawer.value = false;
50
+ setTimeout(() => showMask.value = false, 300);
51
+ }
52
+ });
53
+ const noop = () => {}; // 新增空函数
54
+ const closeDrawer = () => {
55
+ emit('update:modelValue', false);
56
+ };
57
+ </script>
58
+
59
+ <style scoped lang="less">
60
+ .drawer-container {
61
+ position: fixed;
62
+ top: 0;
63
+ left: 0;
64
+ right: 0;
65
+ bottom: 0;
66
+ z-index: 999;
67
+ }
68
+
69
+ .mask {
70
+ position: absolute;
71
+ width: 100%;
72
+ height: 100%;
73
+ background: rgba(0, 0, 0, 0);
74
+ transition: all 0.3s ease;
75
+
76
+ &-show {
77
+ background: rgba(0, 0, 0, 0.5);
78
+ }
79
+ }
80
+
81
+ .drawer-content {
82
+ position: absolute;
83
+ bottom: -100%;
84
+ left: 0;
85
+ right: 0;
86
+ background: #fff;
87
+ border-radius: 24rpx 24rpx 0 0;
88
+ transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
89
+
90
+ &.drawer-up {
91
+ bottom: 0;
92
+ }
93
+ }
94
+
95
+ .close-btn {
96
+ position: absolute;
97
+ top: 16rpx;
98
+ right: 24rpx;
99
+ font-size: 46rpx;
100
+ color: #999;
101
+ padding: 20rpx;
102
+ z-index: 1;
103
+ }
104
+ </style>