@xilonglab/vue-main 1.6.5 → 1.6.6

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.
package/dist/page/app.vue CHANGED
@@ -1,86 +1,86 @@
1
- <script setup>
2
- const props = defineProps({
3
- condition: {
4
- type: Boolean,
5
- default: false
6
- }
7
- })
8
- </script>
9
-
10
- <template>
11
- <div class="user-view" v-if="condition">
12
- <slot name="sidebar" />
13
- <div class="body">
14
- <slot name="topbar" />
15
- <router-view class="content" v-slot="{ Component }">
16
- <keep-alive>
17
- <component :is="Component" />
18
- </keep-alive>
19
- </router-view>
20
- </div>
21
- </div>
22
- <div class="system-view" v-else>
23
- <router-view />
24
- </div>
25
- </template>
26
-
27
- <style lang="less">
28
- body {
29
- background: radial-gradient(circle at 10% 20%, rgb(0, 93, 133) 0%, rgb(0, 181, 149) 90%) !important;
30
- }
31
-
32
- html,
33
- body,
34
- #app,
35
- .user-view,
36
- .system-view {
37
- height: 100%;
38
- width: 100%;
39
- margin: 0;
40
- padding: 0;
41
- max-width: none;
42
- text-align: left;
43
- color: #000;
44
- }
45
-
46
- .user-view {
47
- display: flex;
48
- flex-flow: row;
49
-
50
- .xl-side-bar {
51
- width: 150px;
52
-
53
- .logo-wrapper {
54
- display: flex;
55
- align-items: center;
56
- justify-content: center;
57
- padding-top: 5px;
58
- border-bottom: 1px solid #1c3b64;
59
-
60
- .logo {
61
- opacity: 0.9;
62
- }
63
- }
64
- }
65
-
66
- .body {
67
- flex: 1;
68
- height: 100%;
69
- width: 100%;
70
- margin-right:5px;
71
- display: flex;
72
- flex-flow: column;
73
- overflow-x: scroll;
74
-
75
- .content {
76
- flex: 1;
77
- display: flex;
78
- flex-flow: column;
79
- min-height: 0;
80
- border-radius: 5px;
81
- overflow: hidden;
82
- }
83
-
84
- }
85
- }
86
- </style>
1
+ <script setup>
2
+ const props = defineProps({
3
+ condition: {
4
+ type: Boolean,
5
+ default: false
6
+ }
7
+ })
8
+ </script>
9
+
10
+ <template>
11
+ <div class="user-view" v-if="condition">
12
+ <slot name="sidebar" />
13
+ <div class="body">
14
+ <slot name="topbar" />
15
+ <router-view class="content" v-slot="{ Component }">
16
+ <keep-alive>
17
+ <component :is="Component" />
18
+ </keep-alive>
19
+ </router-view>
20
+ </div>
21
+ </div>
22
+ <div class="system-view" v-else>
23
+ <router-view />
24
+ </div>
25
+ </template>
26
+
27
+ <style lang="less">
28
+ body {
29
+ background: radial-gradient(circle at 10% 20%, rgb(0, 93, 133) 0%, rgb(0, 181, 149) 90%) !important;
30
+ }
31
+
32
+ html,
33
+ body,
34
+ #app,
35
+ .user-view,
36
+ .system-view {
37
+ height: 100%;
38
+ width: 100%;
39
+ margin: 0;
40
+ padding: 0;
41
+ max-width: none;
42
+ text-align: left;
43
+ color: #000;
44
+ }
45
+
46
+ .user-view {
47
+ display: flex;
48
+ flex-flow: row;
49
+
50
+ .xl-side-bar {
51
+ width: 150px;
52
+
53
+ .logo-wrapper {
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ padding-top: 5px;
58
+ border-bottom: 1px solid #1c3b64;
59
+
60
+ .logo {
61
+ opacity: 0.9;
62
+ }
63
+ }
64
+ }
65
+
66
+ .body {
67
+ flex: 1;
68
+ height: 100%;
69
+ width: 100%;
70
+ margin-right:5px;
71
+ display: flex;
72
+ flex-flow: column;
73
+ overflow-x: scroll;
74
+
75
+ .content {
76
+ flex: 1;
77
+ display: flex;
78
+ flex-flow: column;
79
+ min-height: 0;
80
+ border-radius: 5px;
81
+ overflow: hidden;
82
+ }
83
+
84
+ }
85
+ }
86
+ </style>
@@ -1,185 +1,185 @@
1
- <script setup>
2
- import { Session } from '#/system'
3
- import { useRouter } from "vue-router"
4
- import { inject, reactive, ref } from 'vue'
5
- import gsap from 'gsap'
6
-
7
-
8
- const props = defineProps({
9
- callback: {
10
- type: Function,
11
- default: () => {}
12
- }
13
- })
14
-
15
- const router = useRouter()
16
- const store = inject('store')
17
-
18
- const refs = {
19
- form: ref(null),
20
- button: ref(null),
21
- loginBox: ref(null)
22
- }
23
-
24
- const state = reactive({
25
- isRemember: false,
26
- loading: false
27
- })
28
-
29
- const handleKeyEnter = () => refs.button.value.click()
30
-
31
- const showErrorAnimation = () => {
32
- gsap.to(refs.loginBox.value, { duration: 0.1, x: -10, yoyo: true, repeat: 3 })
33
- }
34
-
35
- const showSuccessAnimation = async () => {
36
- await gsap.to(refs.loginBox.value, { duration: 0.5, scale: 0.8, opacity: 0, ease: "power2.in" })
37
- }
38
-
39
- const handleLoginSuccess = ({ token, userData }) => {
40
- store.state.accessToken = token
41
- localStorage.setItem('accessToken', token)
42
- store.state.userData = userData
43
- router.push("/instr/list")
44
- props.callback()
45
- }
46
-
47
- const handleLoginError = (data, { Captcha, Error }) => {
48
- showErrorAnimation()
49
- Captcha.refresh()
50
- const errorMap = {
51
- '用户不存在': Error.user,
52
- '密码错误': Error.password,
53
- '验证码错误': Error.captcha
54
- }
55
- errorMap[data.msg]?.()
56
- }
57
-
58
- async function login() {
59
- state.loading = true
60
- const { form, Captcha, Cookie, Error } = refs.form.value
61
-
62
- try {
63
- if (!await refs.form.value.validate()) return
64
- Cookie.set()
65
- const data = await Session.resource.post(form, '')
66
-
67
- if (data.token) {
68
- await showSuccessAnimation()
69
- handleLoginSuccess(data)
70
- } else if (data.code === 500) {
71
- handleLoginError(data, { Captcha, Error })
72
- }
73
- } finally {
74
- state.loading = false
75
- }
76
- }
77
- </script>
78
-
79
- <template>
80
- <div class="login">
81
- <div class="login-box" ref="loginBox">
82
- <div class="title">
83
- <span class="system-title">__NAME__</span>
84
- <span class="version">V__VERSION__</span>
85
- </div>
86
- <div class="wrapper">
87
- <xl-login-form :ref="refs.form" @keyEnter="handleKeyEnter" :scope="state" />
88
- <xl-async-button class="login-btn" :ref="refs.button" :api="login" :loading="state.loading" style="width: 100%">
89
- {{ state.loading ? '登录中...' : '登录' }}
90
- </xl-async-button>
91
- <div class="tips">
92
- <div class="remember">
93
- <el-checkbox v-model="state.isRemember"></el-checkbox>
94
- <span>记住密码</span>
95
- </div>
96
- </div>
97
- </div>
98
- </div>
99
- </div>
100
- </template>
101
-
102
- <style lang="less">
103
- .login {
104
- display: flex;
105
- justify-content: center;
106
- align-items: center;
107
- width: 100%;
108
- height: 100%;
109
- position: relative;
110
- font-family: "黑体";
111
-
112
- &::before {
113
- content: '';
114
- position: absolute;
115
- inset: 0;
116
- backdrop-filter: blur(5px);
117
- }
118
-
119
- .login-box {
120
- position: relative;
121
- width: 400px;
122
- background: rgba(255, 255, 255, 0.9);
123
- padding: 30px;
124
- border-radius: 15px;
125
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
126
- backdrop-filter: blur(10px);
127
- border: 1px solid rgba(255, 255, 255, 0.2);
128
- transition: transform 0.3s ease;
129
-
130
- &:hover {
131
- transform: translateY(-5px);
132
- }
133
-
134
- .title {
135
- text-align: center;
136
- padding-bottom: 20px;
137
- .system-title {
138
- font-size: 32px;
139
- font-weight: 600;
140
- background: linear-gradient(45deg, #51c7f9, #3a5df7);
141
- -webkit-background-clip: text;
142
- -webkit-text-fill-color: transparent;
143
- display: block;
144
- }
145
- .version {
146
- font-size: 14px;
147
- color: #666;
148
- opacity: 0.8;
149
- }
150
- }
151
-
152
- .login-btn {
153
- background: linear-gradient(45deg, #51c7f9, #3a5df7) !important;
154
- font-size: 18px;
155
- height: 42px;
156
- color: #fff;
157
- border-radius: 21px;
158
- border: none;
159
- transition: all 0.3s ease;
160
-
161
- &:hover {
162
- transform: translateY(-2px);
163
- box-shadow: 0 5px 15px rgba(58,93,247,0.3);
164
- }
165
- }
166
-
167
- .tips {
168
- width: 100%;
169
- padding-top: 20px;
170
- display: flex;
171
- justify-content: space-between;
172
- align-items: center;
173
-
174
- .remember {
175
- cursor: pointer;
176
- color: #666;
177
- font-size: 14px;
178
- display: flex;
179
- align-items: center;
180
- gap: 5px;
181
- }
182
- }
183
- }
184
- }
185
- </style>
1
+ <script setup>
2
+ import { Session } from '#/system'
3
+ import { useRouter } from "vue-router"
4
+ import { inject, reactive, ref } from 'vue'
5
+ import gsap from 'gsap'
6
+
7
+
8
+ const props = defineProps({
9
+ callback: {
10
+ type: Function,
11
+ default: () => {}
12
+ }
13
+ })
14
+
15
+ const router = useRouter()
16
+ const store = inject('store')
17
+
18
+ const refs = {
19
+ form: ref(null),
20
+ button: ref(null),
21
+ loginBox: ref(null)
22
+ }
23
+
24
+ const state = reactive({
25
+ isRemember: false,
26
+ loading: false
27
+ })
28
+
29
+ const handleKeyEnter = () => refs.button.value.click()
30
+
31
+ const showErrorAnimation = () => {
32
+ gsap.to(refs.loginBox.value, { duration: 0.1, x: -10, yoyo: true, repeat: 3 })
33
+ }
34
+
35
+ const showSuccessAnimation = async () => {
36
+ await gsap.to(refs.loginBox.value, { duration: 0.5, scale: 0.8, opacity: 0, ease: "power2.in" })
37
+ }
38
+
39
+ const handleLoginSuccess = ({ token, userData }) => {
40
+ store.state.accessToken = token
41
+ localStorage.setItem('accessToken', token)
42
+ store.state.userData = userData
43
+ router.push("/instr/list")
44
+ props.callback()
45
+ }
46
+
47
+ const handleLoginError = (data, { Captcha, Error }) => {
48
+ showErrorAnimation()
49
+ Captcha.refresh()
50
+ const errorMap = {
51
+ '用户不存在': Error.user,
52
+ '密码错误': Error.password,
53
+ '验证码错误': Error.captcha
54
+ }
55
+ errorMap[data.msg]?.()
56
+ }
57
+
58
+ async function login() {
59
+ state.loading = true
60
+ const { form, Captcha, Cookie, Error } = refs.form.value
61
+
62
+ try {
63
+ if (!await refs.form.value.validate()) return
64
+ Cookie.set()
65
+ const data = await Session.resource.post(form, '')
66
+
67
+ if (data.token) {
68
+ await showSuccessAnimation()
69
+ handleLoginSuccess(data)
70
+ } else if (data.code === 500) {
71
+ handleLoginError(data, { Captcha, Error })
72
+ }
73
+ } finally {
74
+ state.loading = false
75
+ }
76
+ }
77
+ </script>
78
+
79
+ <template>
80
+ <div class="login">
81
+ <div class="login-box" ref="loginBox">
82
+ <div class="title">
83
+ <span class="system-title">__NAME__</span>
84
+ <span class="version">V__VERSION__</span>
85
+ </div>
86
+ <div class="wrapper">
87
+ <xl-login-form :ref="refs.form" @keyEnter="handleKeyEnter" :scope="state" />
88
+ <xl-async-button class="login-btn" :ref="refs.button" :api="login" :loading="state.loading" style="width: 100%">
89
+ {{ state.loading ? '登录中...' : '登录' }}
90
+ </xl-async-button>
91
+ <div class="tips">
92
+ <div class="remember">
93
+ <el-checkbox v-model="state.isRemember"></el-checkbox>
94
+ <span>记住密码</span>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </template>
101
+
102
+ <style lang="less">
103
+ .login {
104
+ display: flex;
105
+ justify-content: center;
106
+ align-items: center;
107
+ width: 100%;
108
+ height: 100%;
109
+ position: relative;
110
+ font-family: "黑体";
111
+
112
+ &::before {
113
+ content: '';
114
+ position: absolute;
115
+ inset: 0;
116
+ backdrop-filter: blur(5px);
117
+ }
118
+
119
+ .login-box {
120
+ position: relative;
121
+ width: 400px;
122
+ background: rgba(255, 255, 255, 0.9);
123
+ padding: 30px;
124
+ border-radius: 15px;
125
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
126
+ backdrop-filter: blur(10px);
127
+ border: 1px solid rgba(255, 255, 255, 0.2);
128
+ transition: transform 0.3s ease;
129
+
130
+ &:hover {
131
+ transform: translateY(-5px);
132
+ }
133
+
134
+ .title {
135
+ text-align: center;
136
+ padding-bottom: 20px;
137
+ .system-title {
138
+ font-size: 32px;
139
+ font-weight: 600;
140
+ background: linear-gradient(45deg, #51c7f9, #3a5df7);
141
+ -webkit-background-clip: text;
142
+ -webkit-text-fill-color: transparent;
143
+ display: block;
144
+ }
145
+ .version {
146
+ font-size: 14px;
147
+ color: #666;
148
+ opacity: 0.8;
149
+ }
150
+ }
151
+
152
+ .login-btn {
153
+ background: linear-gradient(45deg, #51c7f9, #3a5df7) !important;
154
+ font-size: 18px;
155
+ height: 42px;
156
+ color: #fff;
157
+ border-radius: 21px;
158
+ border: none;
159
+ transition: all 0.3s ease;
160
+
161
+ &:hover {
162
+ transform: translateY(-2px);
163
+ box-shadow: 0 5px 15px rgba(58,93,247,0.3);
164
+ }
165
+ }
166
+
167
+ .tips {
168
+ width: 100%;
169
+ padding-top: 20px;
170
+ display: flex;
171
+ justify-content: space-between;
172
+ align-items: center;
173
+
174
+ .remember {
175
+ cursor: pointer;
176
+ color: #666;
177
+ font-size: 14px;
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 5px;
181
+ }
182
+ }
183
+ }
184
+ }
185
+ </style>
@@ -1,72 +1,72 @@
1
- <script setup>
2
- import User from '#/user'
3
- import { ref, reactive, inject } from 'vue'
4
-
5
- const store = inject('store')
6
- const refs = { form: ref(null) }
7
- const data = reactive({ oldPassword: '', newPassword: '', repeatPassword: '' })
8
- const error = reactive({ oldPassword: '', newPassword: '', repeatPassword: '' })
9
-
10
- const rules = {
11
- oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
12
- newPassword: [{ required: true, message: "请输入新密码", trigger: "blur" }],
13
- repeatPassword: [{ required: true, message: "请再确认密码", trigger: "blur" }],
14
- }
15
-
16
- const confirm = async () => {
17
- const isValid = await refs.form.value.validate()
18
- if (!isValid) return
19
-
20
- if (data.newPassword !== data.repeatPassword) {
21
- error.repeatPassword = '两次输入不一致'
22
- } else {
23
- const { oldPassword, newPassword } = data
24
- const rsp = await User.Password.resource.put({
25
- userId: store.state.userData.id,
26
- oldPassword,
27
- newPassword
28
- })
29
- if (rsp?.code === 500 && rsp.msg === '原密码错误') {
30
- error.oldPassword = '原密码错误'
31
- } else {
32
- Object.keys(data).forEach(key => data[key] = '')
33
- }
34
- }
35
- }
36
- </script>
37
-
38
- <template>
39
- <div class="setting">
40
- <div class="wrapper">
41
- <el-form class="form" :ref="refs.form" :model="data" :rules="rules">
42
- <el-form-item label="原密码" prop="oldPassword" :error="error.oldPassword">
43
- <xl-input type="password" v-model="data.oldPassword" placeholder="" style="width:100%"/>
44
- </el-form-item>
45
- <el-form-item label="新密码" prop="newPassword" :error="error.newPassword">
46
- <xl-input type="password" v-model="data.newPassword" placeholder="" style="width:100%"/>
47
- </el-form-item>
48
- <el-form-item label="请确认" prop="repeatPassword" :error="error.repeatPassword">
49
- <xl-input type="password" v-model="data.repeatPassword" placeholder="" style="width:100%"/>
50
- </el-form-item>
51
- <xl-button type="primary" @click="confirm">确定</xl-button>
52
- </el-form>
53
- </div>
54
- </div>
55
- </template>
56
-
57
- <style lang="less">
58
- .setting {
59
- background: #fff;
60
- .wrapper {
61
- height: 100%;
62
- display: flex;
63
- align-items: center;
64
- justify-content: center;
65
- .form {
66
- width: 360px;
67
- height: 300px;
68
- text-align: center;
69
- }
70
- }
71
- }
1
+ <script setup>
2
+ import User from '#/user'
3
+ import { ref, reactive, inject } from 'vue'
4
+
5
+ const store = inject('store')
6
+ const refs = { form: ref(null) }
7
+ const data = reactive({ oldPassword: '', newPassword: '', repeatPassword: '' })
8
+ const error = reactive({ oldPassword: '', newPassword: '', repeatPassword: '' })
9
+
10
+ const rules = {
11
+ oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
12
+ newPassword: [{ required: true, message: "请输入新密码", trigger: "blur" }],
13
+ repeatPassword: [{ required: true, message: "请再确认密码", trigger: "blur" }],
14
+ }
15
+
16
+ const confirm = async () => {
17
+ const isValid = await refs.form.value.validate()
18
+ if (!isValid) return
19
+
20
+ if (data.newPassword !== data.repeatPassword) {
21
+ error.repeatPassword = '两次输入不一致'
22
+ } else {
23
+ const { oldPassword, newPassword } = data
24
+ const rsp = await User.Password.resource.put({
25
+ userId: store.state.userData.id,
26
+ oldPassword,
27
+ newPassword
28
+ })
29
+ if (rsp?.code === 500 && rsp.msg === '原密码错误') {
30
+ error.oldPassword = '原密码错误'
31
+ } else {
32
+ Object.keys(data).forEach(key => data[key] = '')
33
+ }
34
+ }
35
+ }
36
+ </script>
37
+
38
+ <template>
39
+ <div class="setting">
40
+ <div class="wrapper">
41
+ <el-form class="form" :ref="refs.form" :model="data" :rules="rules">
42
+ <el-form-item label="原密码" prop="oldPassword" :error="error.oldPassword">
43
+ <xl-input type="password" v-model="data.oldPassword" placeholder="" style="width:100%"/>
44
+ </el-form-item>
45
+ <el-form-item label="新密码" prop="newPassword" :error="error.newPassword">
46
+ <xl-input type="password" v-model="data.newPassword" placeholder="" style="width:100%"/>
47
+ </el-form-item>
48
+ <el-form-item label="请确认" prop="repeatPassword" :error="error.repeatPassword">
49
+ <xl-input type="password" v-model="data.repeatPassword" placeholder="" style="width:100%"/>
50
+ </el-form-item>
51
+ <xl-button l="确定" type="primary" @click="confirm"/>
52
+ </el-form>
53
+ </div>
54
+ </div>
55
+ </template>
56
+
57
+ <style lang="less">
58
+ .setting {
59
+ background: #fff;
60
+ .wrapper {
61
+ height: 100%;
62
+ display: flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ .form {
66
+ width: 360px;
67
+ height: 300px;
68
+ text-align: center;
69
+ }
70
+ }
71
+ }
72
72
  </style>
@@ -1,41 +1,41 @@
1
- :root {
2
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
- line-height: 1.5;
4
- font-weight: 400;
5
-
6
- color-scheme: light dark;
7
- color: rgba(255, 255, 255, 0.87);
8
- background-color: #242424;
9
-
10
- font-synthesis: none;
11
- text-rendering: optimizeLegibility;
12
- -webkit-font-smoothing: antialiased;
13
- -moz-osx-font-smoothing: grayscale;
14
- -webkit-text-size-adjust: 100%;
15
- }
16
-
17
- html,
18
- body,
19
- #app {
20
- height: 100%;
21
- width: 100%;
22
- margin: 0;
23
- padding: 0;
24
- }
25
-
26
- input[type=number]::-webkit-inner-spin-button,
27
- input[type=number]::-webkit-outer-spin-button {
28
- -webkit-appearance: none;
29
- appearance: none;
30
- margin: 0;
31
- }
32
-
33
- ::-webkit-scrollbar {
34
- width: 10px;
35
- height: 10px;
36
- }
37
-
38
- ::-webkit-scrollbar-thumb {
39
- background-color: #e7ebf5;
40
- border-radius: 10px;
1
+ :root {
2
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ -webkit-text-size-adjust: 100%;
15
+ }
16
+
17
+ html,
18
+ body,
19
+ #app {
20
+ height: 100%;
21
+ width: 100%;
22
+ margin: 0;
23
+ padding: 0;
24
+ }
25
+
26
+ input[type=number]::-webkit-inner-spin-button,
27
+ input[type=number]::-webkit-outer-spin-button {
28
+ -webkit-appearance: none;
29
+ appearance: none;
30
+ margin: 0;
31
+ }
32
+
33
+ ::-webkit-scrollbar {
34
+ width: 10px;
35
+ height: 10px;
36
+ }
37
+
38
+ ::-webkit-scrollbar-thumb {
39
+ background-color: #e7ebf5;
40
+ border-radius: 10px;
41
41
  }
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
- {
2
- "name": "@xilonglab/vue-main",
3
- "version": "1.6.5",
4
- "description": "xilong vue main",
5
- "main": "packages/index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "author": "xilonglab",
10
- "license": "ISC",
11
- "dependencies": {
12
- "@imengyu/vue3-context-menu": "^1.3.3",
13
- "element-plus": "2.3.6",
14
- "image-conversion": "^2.1.1",
15
- "vue-router": "^4.2.2"
16
- }
1
+ {
2
+ "name": "@xilonglab/vue-main",
3
+ "version": "1.6.6",
4
+ "description": "xilong vue main",
5
+ "main": "packages/index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "xilonglab",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "@imengyu/vue3-context-menu": "^1.3.3",
13
+ "element-plus": "2.3.6",
14
+ "image-conversion": "^2.1.1",
15
+ "vue-router": "^4.2.2"
16
+ }
17
17
  }
@@ -1,3 +1,235 @@
1
+ <<<<<<< HEAD
2
+ <script setup>
3
+ defineOptions({ name: "XlButton" })
4
+
5
+ const props = defineProps({
6
+ type: {},
7
+ l: {
8
+ type: String,
9
+ default: ''
10
+ },
11
+ size: {},
12
+ disabled: {},
13
+ loading: {},
14
+ icon: {},
15
+ trigger: {
16
+ type: Function,
17
+ default: null
18
+ }
19
+ });
20
+
21
+ function handleClick(event) {
22
+ if (props.trigger) {
23
+ props.trigger();
24
+ }
25
+ }
26
+ </script>
27
+
28
+
29
+ <template>
30
+ <el-button
31
+ class="xl-button"
32
+ :size="size"
33
+ :type="type"
34
+ :disabled="disabled"
35
+ :loading="loading?.value ?? loading ?? false"
36
+ :icon="icon"
37
+ @click="handleClick"
38
+ >
39
+ <span>{{ l }}</span>
40
+ <slot />
41
+ </el-button>
42
+ </template>
43
+
44
+
45
+ <style lang="less">
46
+ .xl-button {
47
+ position: relative;
48
+ overflow: hidden;
49
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
50
+ border-radius: 5px;
51
+ font-weight: 500;
52
+
53
+ // 默认按钮科技感样式
54
+ &.el-button--default {
55
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
56
+ border: 1px solid #dee2e6;
57
+ color: #495057;
58
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
59
+
60
+ &:hover {
61
+ background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
62
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
63
+ transform: translateY(-1px);
64
+ }
65
+
66
+ &:active {
67
+ transform: translateY(0);
68
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
69
+ }
70
+ }
71
+
72
+ // 主要按钮科技感样式
73
+ &.el-button--primary {
74
+ background: linear-gradient(135deg, #409eff 0%, #337ecc 100%);
75
+ border: 1px solid #409eff;
76
+ color: #ffffff;
77
+ box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
78
+
79
+ &:hover {
80
+ background: linear-gradient(135deg, #337ecc 0%, #2b6cb0 100%);
81
+ box-shadow: 0 4px 16px rgba(64, 158, 255, 0.4);
82
+ transform: translateY(-1px);
83
+ }
84
+
85
+ &:active {
86
+ transform: translateY(0);
87
+ box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
88
+ }
89
+ }
90
+
91
+ // 成功按钮科技感样式
92
+ &.el-button--success {
93
+ background: linear-gradient(135deg, #67c23a 0%, #529b2e 100%);
94
+ border: 1px solid #67c23a;
95
+ color: #ffffff;
96
+ box-shadow: 0 2px 8px rgba(103, 194, 58, 0.3);
97
+
98
+ &:hover {
99
+ background: linear-gradient(135deg, #529b2e 0%, #4a7c59 100%);
100
+ box-shadow: 0 4px 16px rgba(103, 194, 58, 0.4);
101
+ transform: translateY(-1px);
102
+ }
103
+
104
+ &:active {
105
+ transform: translateY(0);
106
+ box-shadow: 0 2px 8px rgba(103, 194, 58, 0.3);
107
+ }
108
+ }
109
+
110
+ // 警告按钮科技感样式
111
+ &.el-button--warning {
112
+ background: linear-gradient(135deg, #e6a23c 0%, #d4941e 100%);
113
+ border: 1px solid #e6a23c;
114
+ color: #ffffff;
115
+ box-shadow: 0 2px 8px rgba(230, 162, 60, 0.3);
116
+
117
+ &:hover {
118
+ background: linear-gradient(135deg, #d4941e 0%, #b8860b 100%);
119
+ box-shadow: 0 4px 16px rgba(230, 162, 60, 0.4);
120
+ transform: translateY(-1px);
121
+ }
122
+
123
+ &:active {
124
+ transform: translateY(0);
125
+ box-shadow: 0 2px 8px rgba(230, 162, 60, 0.3);
126
+ }
127
+ }
128
+
129
+ // 危险按钮科技感样式
130
+ &.el-button--danger {
131
+ background: linear-gradient(135deg, #f56c6c 0%, #e53e3e 100%);
132
+ border: 1px solid #f56c6c;
133
+ color: #ffffff;
134
+ box-shadow: 0 2px 8px rgba(245, 108, 108, 0.3);
135
+
136
+ &:hover {
137
+ background: linear-gradient(135deg, #e53e3e 0%, #c53030 100%);
138
+ box-shadow: 0 4px 16px rgba(245, 108, 108, 0.4);
139
+ transform: translateY(-1px);
140
+ }
141
+
142
+ &:active {
143
+ transform: translateY(0);
144
+ box-shadow: 0 2px 8px rgba(245, 108, 108, 0.3);
145
+ }
146
+ }
147
+
148
+ // 信息按钮科技感样式
149
+ &.el-button--info {
150
+ background: linear-gradient(135deg, #909399 0%, #73767a 100%);
151
+ border: 1px solid #909399;
152
+ color: #ffffff;
153
+ box-shadow: 0 2px 8px rgba(144, 147, 153, 0.3);
154
+
155
+ &:hover {
156
+ background: linear-gradient(135deg, #73767a 0%, #5a5c5e 100%);
157
+ box-shadow: 0 4px 16px rgba(144, 147, 153, 0.4);
158
+ transform: translateY(-1px);
159
+ }
160
+
161
+ &:active {
162
+ transform: translateY(0);
163
+ box-shadow: 0 2px 8px rgba(144, 147, 153, 0.3);
164
+ }
165
+ }
166
+
167
+ // 禁用状态
168
+ &.is-disabled {
169
+ opacity: 0.6;
170
+ cursor: not-allowed;
171
+ transform: none !important;
172
+ box-shadow: none !important;
173
+
174
+ &:hover {
175
+ transform: none !important;
176
+ box-shadow: none !important;
177
+ }
178
+ }
179
+
180
+ // 加载状态
181
+ &.is-loading {
182
+ position: relative;
183
+
184
+ &::before {
185
+ content: '';
186
+ position: absolute;
187
+ top: 0;
188
+ left: 0;
189
+ right: 0;
190
+ bottom: 0;
191
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
192
+ animation: shimmer 1.5s infinite;
193
+ }
194
+ }
195
+
196
+ // 发光效果(用于特殊场景)
197
+ &.xl-button--glow {
198
+ &::after {
199
+ content: '';
200
+ position: absolute;
201
+ top: -2px;
202
+ left: -2px;
203
+ right: -2px;
204
+ bottom: -2px;
205
+ background: inherit;
206
+ border-radius: inherit;
207
+ filter: blur(4px);
208
+ opacity: 0.3;
209
+ z-index: -1;
210
+ }
211
+ }
212
+ }
213
+
214
+ // 闪烁动画
215
+ @keyframes shimmer {
216
+ 0% {
217
+ transform: translateX(-100%);
218
+ }
219
+ 100% {
220
+ transform: translateX(100%);
221
+ }
222
+ }
223
+
224
+ // 响应式设计
225
+ @media (max-width: 768px) {
226
+ .xl-button {
227
+ font-size: 14px;
228
+ padding: 8px 16px;
229
+ }
230
+ }
231
+ </style>
232
+ =======
1
233
  <script setup>
2
234
  defineOptions({ name: "XlButton" })
3
235
 
@@ -228,3 +460,4 @@ function handleClick(event) {
228
460
  }
229
461
  }
230
462
  </style>
463
+ >>>>>>> 10f1033bebbe3355872fa734591c08b3ae78df9d
@@ -17,7 +17,6 @@ defineOptions({ name: "XlLoadingView" })
17
17
  display: flex;
18
18
  align-items: center;
19
19
  justify-content: center;
20
- background: radial-gradient(circle at 10% 20%, rgb(0, 93, 133) 0%, rgb(0, 181, 149) 90%);
21
20
  height: 100%;
22
21
  width: 100%;
23
22
 
@@ -1,147 +1,148 @@
1
- <script setup>
2
- defineOptions({ name: "XlTabsNav" })
3
-
4
- const props = defineProps({
5
- /** 当前激活 tab 的背景色,默认淡绿色 */
6
- activeBgColor: {
7
- type: String,
8
- default: 'rgb(66, 185, 131)'
9
- }
10
- })
11
-
12
- import { useRouter } from 'vue-router'
13
- import { inject } from 'vue'
14
- import ContextMenu from '@imengyu/vue3-context-menu'
15
-
16
- const router = useRouter()
17
- const store = inject('store')
18
-
19
- const switchTab = (tab) => {
20
- router.push(tab.fullPath)
21
- store.setActiveTab(tab.fullPath, tab.query)
22
- }
23
-
24
- const closeTab = (path) => {
25
- store.closeTab(path, router)
26
- }
27
-
28
- const closeOtherTabs = (currentPath) => {
29
- store.closeOtherTabs(currentPath, router)
30
- }
31
-
32
- const closeLeftTabs = (currentPath) => {
33
- store.closeLeftTabs(currentPath, router)
34
- }
35
-
36
- const closeRightTabs = (currentPath) => {
37
- store.closeRightTabs(currentPath, router)
38
- }
39
-
40
- const showContextMenu = (event, tab) => {
41
- event.preventDefault()
42
-
43
- const currentIndex = store.state.tabs.findIndex(t => t.fullPath === tab.fullPath)
44
- const hasLeftTabs = currentIndex > 0
45
- const hasRightTabs = currentIndex >= 0 && currentIndex < store.state.tabs.length - 1
46
-
47
- const menuItems = []
48
-
49
- if (hasLeftTabs) {
50
- menuItems.push({
51
- label: '关闭左侧标签页',
52
- onClick: () => closeLeftTabs(tab.fullPath)
53
- })
54
- }
55
-
56
- if (hasRightTabs) {
57
- menuItems.push({
58
- label: '关闭右侧标签页',
59
- onClick: () => closeRightTabs(tab.fullPath)
60
- })
61
- }
62
-
63
- if (store.state.tabs.length > 1) {
64
- menuItems.push({
65
- label: '关闭其它标签页',
66
- onClick: () => closeOtherTabs(tab.fullPath)
67
- })
68
- }
69
-
70
- if (menuItems.length > 0) {
71
- ContextMenu.showContextMenu({
72
- x: event.x,
73
- y: event.y,
74
- items: menuItems
75
- })
76
- }
77
- }
78
- </script>
79
-
80
-
81
- <template>
82
- <div class="xl-tabs-nav">
83
- <div
84
- v-for="tab in store.state.tabs"
85
- :key="tab.path"
86
- class="tab-item"
87
- :class="{ active: tab.fullPath === store.state.activeTab }"
88
- :style="tab.fullPath === store.state.activeTab ? { background: props.activeBgColor } : undefined"
89
- @click="switchTab(tab)"
90
- @contextmenu="showContextMenu($event, tab)"
91
- >
92
- <span>{{ tab.title }}</span>
93
- <span class="close-icon" @click.stop="closeTab(tab.fullPath)">&times;</span>
94
- </div>
95
- </div>
96
- </template>
97
-
98
-
99
- <style lang="less">
100
- .xl-tabs-nav {
101
- display: flex;
102
- padding: 4px 4px 0;
103
-
104
- .tab-item {
105
- height:20px;
106
-
107
- padding: 5px 10px;
108
- background: #fff;
109
- color:rgb(100,100,100);
110
- margin-right: 3px;
111
- // border: 1px solid #d9d9d9;
112
- /* border-bottom: none; */
113
- border-radius: 4px;
114
- cursor: pointer;
115
- display: flex;
116
- align-items: center;
117
- font-size: 14px;
118
- color:rgb(100,100,100);
119
- /* gap: 8px; */
120
- }
121
-
122
- .tab-item.active {
123
- /* background 由 props.activeBgColor 通过内联 style 控制,默认淡绿色 */
124
- color:#fff;
125
- }
126
-
127
- .close-icon {
128
- font-size: 14px;
129
- width: 16px;
130
- height: 16px;
131
- line-height: 16px;
132
- text-align: center;
133
- border-radius: 50%;
134
- }
135
-
136
- .close-icon:hover {
137
- background: #ccc;
138
- }
139
-
140
- .tabs-content {
141
- flex: 1;
142
- display: flex;
143
- flex-flow: column;
144
- overflow: auto;
145
- }
146
- }
1
+ <script setup>
2
+ defineOptions({ name: "XlTabsNav" })
3
+
4
+ import { useRouter } from 'vue-router'
5
+ import { inject } from 'vue'
6
+ import ContextMenu from '@imengyu/vue3-context-menu'
7
+
8
+ const props = defineProps({
9
+ /** 当前激活 tab 的背景色,默认淡绿色 */
10
+ activeBgColor: {
11
+ type: String,
12
+ default: 'rgb(66, 185, 131)'
13
+ }
14
+ })
15
+
16
+ const router = useRouter()
17
+ const store = inject('store')
18
+
19
+ const switchTab = (tab) => {
20
+ router.push(tab.fullPath)
21
+ store.setActiveTab(tab.fullPath, tab.query)
22
+ }
23
+
24
+ const closeTab = (path) => {
25
+ store.closeTab(path, router)
26
+ }
27
+
28
+ const closeOtherTabs = (currentPath) => {
29
+ store.closeOtherTabs(currentPath, router)
30
+ }
31
+
32
+ const closeLeftTabs = (currentPath) => {
33
+ store.closeLeftTabs(currentPath, router)
34
+ }
35
+
36
+ const closeRightTabs = (currentPath) => {
37
+ store.closeRightTabs(currentPath, router)
38
+ }
39
+
40
+ const showContextMenu = (event, tab) => {
41
+ event.preventDefault()
42
+
43
+ const currentIndex = store.state.tabs.findIndex(t => t.fullPath === tab.fullPath)
44
+ const hasLeftTabs = currentIndex > 0
45
+ const hasRightTabs = currentIndex >= 0 && currentIndex < store.state.tabs.length - 1
46
+
47
+ const menuItems = []
48
+
49
+ if (hasLeftTabs) {
50
+ menuItems.push({
51
+ label: '关闭左侧标签页',
52
+ onClick: () => closeLeftTabs(tab.fullPath)
53
+ })
54
+ }
55
+
56
+ if (hasRightTabs) {
57
+ menuItems.push({
58
+ label: '关闭右侧标签页',
59
+ onClick: () => closeRightTabs(tab.fullPath)
60
+ })
61
+ }
62
+
63
+ if (store.state.tabs.length > 1) {
64
+ menuItems.push({
65
+ label: '关闭其它标签页',
66
+ onClick: () => closeOtherTabs(tab.fullPath)
67
+ })
68
+ }
69
+
70
+ if (menuItems.length > 0) {
71
+ ContextMenu.showContextMenu({
72
+ x: event.x,
73
+ y: event.y,
74
+ items: menuItems
75
+ })
76
+ }
77
+ }
78
+ </script>
79
+
80
+
81
+ <template>
82
+ <div class="xl-tabs-nav">
83
+ <div
84
+ v-for="tab in store.state.tabs"
85
+ :key="tab.path"
86
+ class="tab-item"
87
+ :class="{ active: tab.fullPath === store.state.activeTab }"
88
+ :style="tab.fullPath === store.state.activeTab ? { background: props.activeBgColor } : undefined"
89
+ @click="switchTab(tab)"
90
+ @contextmenu="showContextMenu($event, tab)"
91
+ >
92
+ <span>{{ tab.title }}</span>
93
+ <span class="close-icon" @click.stop="closeTab(tab.fullPath)">&times;</span>
94
+ </div>
95
+ </div>
96
+ </template>
97
+
98
+
99
+ <style lang="less">
100
+ .xl-tabs-nav {
101
+ display: flex;
102
+ padding: 4px 4px 0;
103
+
104
+ .tab-item {
105
+ height:20px;
106
+
107
+ padding: 5px 10px;
108
+ background: #fff;
109
+ color:rgb(100,100,100);
110
+ margin-right: 3px;
111
+ // border: 1px solid #d9d9d9;
112
+ /* border-bottom: none; */
113
+ border-radius: 4px;
114
+ cursor: pointer;
115
+ display: flex;
116
+ align-items: center;
117
+ font-size: 14px;
118
+ color:rgb(100,100,100);
119
+ /* gap: 8px; */
120
+ }
121
+
122
+ .tab-item.active {
123
+ /* background 由 props.activeBgColor 通过内联 style 控制,默认淡绿色 */
124
+ /* background 由 props.activeBgColor 通过内联 style 控制,默认淡绿色 */
125
+ color:#fff;
126
+ }
127
+
128
+ .close-icon {
129
+ font-size: 14px;
130
+ width: 16px;
131
+ height: 16px;
132
+ line-height: 16px;
133
+ text-align: center;
134
+ border-radius: 50%;
135
+ }
136
+
137
+ .close-icon:hover {
138
+ background: #ccc;
139
+ }
140
+
141
+ .tabs-content {
142
+ flex: 1;
143
+ display: flex;
144
+ flex-flow: column;
145
+ overflow: auto;
146
+ }
147
+ }
147
148
  </style>