component_ryl_v3 1.1.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.
package/src/main.js ADDED
@@ -0,0 +1,11 @@
1
+ import { createApp } from 'vue'
2
+ import 'element-plus/dist/index.css'
3
+ import "@/assets/styles/index.scss"
4
+ import "@/assets/styles/element-plus-theme.scss"
5
+ import App from './App.vue'
6
+ import directives from './packages/directives'
7
+
8
+ const app = createApp(App)
9
+
10
+ app.use(directives)
11
+ app.mount('#app')
@@ -0,0 +1,47 @@
1
+ export default {
2
+ mounted(el, binding) {
3
+ const [text, callback] = Array.isArray(binding.value) ? binding.value : [binding.value]
4
+
5
+ el.$copyText = text
6
+
7
+ const handleClick = async () => {
8
+ if (!el.$copyText) {
9
+ callback?.('暂无复制内容', false)
10
+ return
11
+ }
12
+
13
+ try {
14
+ await navigator.clipboard.writeText(el.$copyText)
15
+ callback?.(null, true)
16
+ } catch {
17
+ const textarea = document.createElement('textarea')
18
+ textarea.value = el.$copyText
19
+ textarea.style.position = 'absolute'
20
+ textarea.style.left = '-9999px'
21
+ document.body.appendChild(textarea)
22
+ textarea.select()
23
+
24
+ try {
25
+ document.execCommand('copy')
26
+ callback?.(null, true)
27
+ } catch (err) {
28
+ callback?.(err.message || '复制失败', false)
29
+ } finally {
30
+ document.body.removeChild(textarea)
31
+ }
32
+ }
33
+ }
34
+
35
+ el.addEventListener('click', handleClick)
36
+ el.$copyHandler = handleClick
37
+ },
38
+
39
+ updated(el, binding) {
40
+ const [text] = Array.isArray(binding.value) ? binding.value : [binding.value]
41
+ el.$copyText = text
42
+ },
43
+
44
+ unmounted(el) {
45
+ el.removeEventListener('click', el.$copyHandler)
46
+ }
47
+ }
@@ -0,0 +1,17 @@
1
+ export default {
2
+ mounted(el, binding) {
3
+ const { value, arg = 'click' } = binding
4
+ const [fn, delay = 300, ...args] = Array.isArray(value) ? value : [value]
5
+
6
+ let timer = null
7
+
8
+ el.addEventListener(arg, () => {
9
+ if (timer) {
10
+ clearTimeout(timer)
11
+ }
12
+ timer = setTimeout(() => {
13
+ fn.apply(el, args)
14
+ }, delay)
15
+ })
16
+ }
17
+ }
@@ -0,0 +1,18 @@
1
+ import money from './money'
2
+ import copy from './copy'
3
+ import debounce from './debounce'
4
+ import throttle from './throttle'
5
+ let directives = {
6
+ money,
7
+ debounce,
8
+ throttle,
9
+ copy
10
+ }
11
+
12
+ export default {
13
+ install(app) {
14
+ Object.keys(directives).forEach((key) => {
15
+ app.directive(key, directives[key])
16
+ })
17
+ },
18
+ }
@@ -0,0 +1,84 @@
1
+ export default {
2
+ mounted(el, binding) {
3
+ const input = el.querySelector('input') || el
4
+
5
+ const handleInput = () => {
6
+ let value = input.value
7
+
8
+ if (!value) return
9
+
10
+ if (value.startsWith('.')) {
11
+ value = '0.'
12
+ }
13
+
14
+ value = value.replace(/[^\d.]/g, '')
15
+
16
+ const parts = value.split('.')
17
+ if (parts.length > 2) {
18
+ value = parts[0] + '.' + parts.slice(1).join('')
19
+ }
20
+
21
+ if (parts[0] && parts[0].length > 1 && parts[0].startsWith('0')) {
22
+ parts[0] = parts[0].replace(/^0+/, '') || '0'
23
+ value = parts.length === 1 ? parts[0] : parts[0] + '.' + parts[1]
24
+ }
25
+
26
+ if (parts[1] && parts[1].length > 2) {
27
+ value = parts[0] + '.' + parts[1].substring(0, 2)
28
+ }
29
+
30
+ if (binding.value) {
31
+ const { min = 0, max } = binding.value
32
+
33
+ const numValue = parseFloat(value) || 0
34
+
35
+ if (!isNaN(numValue)) {
36
+ if (numValue < min) {
37
+ value = String(min)
38
+ } else if (max !== undefined && numValue > max) {
39
+ value = String(max)
40
+ }
41
+ }
42
+ }
43
+
44
+ if (input.value !== value) {
45
+ input.value = value
46
+ input.dispatchEvent(new Event('input'))
47
+ }
48
+ }
49
+
50
+ const handleBlur = () => {
51
+ let value = input.value
52
+
53
+ if (!value) return
54
+
55
+ const parts = value.split('.')
56
+
57
+ if (parts[1] && parts[1].length === 0) {
58
+ value = parts[0] || '0'
59
+ }
60
+
61
+ if (binding.value) {
62
+ const { min = 0, max } = binding.value
63
+
64
+ const numValue = parseFloat(value) || 0
65
+
66
+ if (!isNaN(numValue)) {
67
+ if (numValue < min) {
68
+ value = String(min)
69
+ } else if (max !== undefined && numValue > max) {
70
+ value = String(max)
71
+ }
72
+ }
73
+ }
74
+
75
+ if (input.value !== value) {
76
+ input.value = value
77
+ input.dispatchEvent(new Event('input'))
78
+ }
79
+ }
80
+
81
+ input.addEventListener('input', handleInput)
82
+ input.addEventListener('blur', handleBlur)
83
+ }
84
+ }
@@ -0,0 +1,29 @@
1
+ export default {
2
+ mounted(el, binding) {
3
+ const { value, arg = 'click' } = binding
4
+ const [fn, delay = 300, ...args] = Array.isArray(value) ? value : [value]
5
+
6
+ let timer = null
7
+ let lastTime = 0
8
+
9
+ el.addEventListener(arg, () => {
10
+ const now = Date.now()
11
+ const remaining = delay - (now - lastTime)
12
+
13
+ if (remaining <= 0 || remaining > delay) {
14
+ if (timer) {
15
+ clearTimeout(timer)
16
+ timer = null
17
+ }
18
+ lastTime = now
19
+ fn.apply(el, args)
20
+ } else if (!timer) {
21
+ timer = setTimeout(() => {
22
+ lastTime = Date.now()
23
+ timer = null
24
+ fn.apply(el, args)
25
+ }, remaining)
26
+ }
27
+ })
28
+ }
29
+ }
@@ -0,0 +1,79 @@
1
+ import { ElMessageBox } from "element-plus";
2
+
3
+ /**
4
+ * 二次封装 Element Plus MessageBox
5
+ *
6
+ * @param {Object} options - 配置选项
7
+ * @param {number} [options.type=1] - 类型:1(自定义/常用样式), 2(默认)
8
+ * @param {string} options.text - 提示内容
9
+ * @param {string} [options.title=''] - 提示标题(type为2时才传)
10
+ * @param {boolean} [options.showCancelButton=true] - 是否显示取消按钮
11
+ * @param {string} [options.icon='warning'] - 图标
12
+ * @param {string} [options.confirmButtonText='确定'] - 确认按钮文本
13
+ * @param {string} [options.cancelButtonText='取消'] - 取消按钮文本
14
+ * @param {string} [options.customClass=''] - 自定义样式类
15
+ * @param {boolean} [options.dangerouslyUseHTMLString=false] - 是否把传入的文案当成html处理
16
+ * @returns {Promise} - Promise对象,resolve表示确认,reject表示取消
17
+ */
18
+ export function handConfirmNew({
19
+ type = 1,
20
+ text,
21
+ title = '',
22
+ showCancelButton = true,
23
+ icon = 'warning',
24
+ confirmButtonText = '确定',
25
+ cancelButtonText = '取消',
26
+ customClass = '',
27
+ dangerouslyUseHTMLString = false
28
+ }) {
29
+ return new Promise((resolve, reject) => {
30
+ if (type === 1) {
31
+ return ElMessageBox.confirm(text, '', {
32
+ showCancelButton,
33
+ type: icon,
34
+ confirmButtonText,
35
+ cancelButtonText,
36
+ closeOnClickModal: false,
37
+ center: true,
38
+ dangerouslyUseHTMLString,
39
+ customClass: 'initMsg_in ' + customClass
40
+ })
41
+ .then(() => {
42
+ setTimeout(() => {
43
+ resolve();
44
+ }, 200);
45
+ })
46
+ .catch(() => {
47
+ setTimeout(() => {
48
+ reject();
49
+ }, 200);
50
+ });
51
+ } else {
52
+ return ElMessageBox.confirm(text, title, {
53
+ showCancelButton,
54
+ type: icon,
55
+ confirmButtonText,
56
+ cancelButtonText,
57
+ closeOnClickModal: false,
58
+ dangerouslyUseHTMLString,
59
+ distinguishCancelAndClose: true,
60
+ customClass: 'defaultMsg_in ' + customClass
61
+ })
62
+ .then(() => {
63
+ setTimeout(() => {
64
+ resolve();
65
+ }, 200);
66
+ })
67
+ .catch((action) => {
68
+ // 只有点 取消 按钮,才 reject()
69
+ if (action === 'cancel') {
70
+ reject();
71
+ }
72
+ });
73
+ }
74
+ });
75
+ }
76
+
77
+ export default {
78
+ handConfirmNew
79
+ }
@@ -0,0 +1,204 @@
1
+ <template>
2
+ <div>
3
+ <!-- 弹窗组件 -->
4
+ <ElDialog
5
+ :modal-class="'jddsDialog '+customClass"
6
+ v-model="show"
7
+ :width="width + 'px'"
8
+ :close-on-click-modal="false"
9
+ :append-to-body="true"
10
+ :show-close="showClose"
11
+ :center="center"
12
+ align-center
13
+ @close="handleClose"
14
+ >
15
+ <!-- 弹窗标题 -->
16
+ <template #header>
17
+ <div class="header-title">
18
+ <img :src="title_img_url" :style="title_img_style" :class="title_img_class"/>
19
+ <span class="zbt">{{title}}</span>
20
+ </div>
21
+ </template>
22
+
23
+ <!-- 弹窗内容插槽 -->
24
+ <slot name="content" />
25
+
26
+ <!-- 弹窗底部按钮区域 -->
27
+ <template #footer>
28
+ <template v-if="btnCount == 2">
29
+ <ElButton @click="exit" class="btn exit" :class="cancelTextClass" :style="cancelTextStyle">
30
+ {{ cancelText == '关闭' ? '取消' : cancelText }}
31
+ </ElButton>
32
+ <ElButton
33
+ type="primary"
34
+ class="btn save"
35
+ :class="confirmTextClass"
36
+ :style="confirmTextStyle"
37
+ :loading="loading"
38
+ @click="save"
39
+ >
40
+ {{ confirmText }}
41
+ </ElButton>
42
+ </template>
43
+ <template v-if="btnCount === 1">
44
+ <ElButton @click="exit" class="btn exit" :class="cancelTextClass" :style="cancelTextStyle">
45
+ {{ cancelText }}
46
+ </ElButton>
47
+ </template>
48
+ </template>
49
+ </ElDialog>
50
+ </div>
51
+ </template>
52
+
53
+ <script setup>
54
+ /**
55
+ * 弹窗组件
56
+ * 功能:提供通用弹窗封装,支持自定义标题、按钮、样式等配置
57
+ */
58
+
59
+ // 导入Vue响应式API
60
+ import { ref, watch } from 'vue'
61
+ // 导入Element Plus弹窗组件
62
+ import { ElDialog, ElButton } from 'element-plus'
63
+
64
+ /**
65
+ * 组件属性定义
66
+ * @property {string} customClass - 自定义样式类名
67
+ * @property {boolean} showClose - 是否显示右上角关闭按钮
68
+ * @property {boolean} modelValue - 弹窗显示状态(v-model 默认prop)
69
+ * @property {string} title - 弹窗标题
70
+ * @property {string} title_img_url - 标题图片地址
71
+ * @property {string} title_img_style - 标题图片样式
72
+ * @property {string} title_img_class - 标题图片类名
73
+ * @property {number} width - 弹窗宽度(像素)
74
+ * @property {boolean} center - 是否居中布局
75
+ * @property {number} btnCount - 按钮数量(0/1/2)
76
+ * @property {string} confirmText - 确定按钮文本
77
+ * @property {string} cancelText - 取消按钮文本
78
+ * @property {string} cancelTextClass - 取消按钮类名
79
+ * @property {string} cancelTextStyle - 取消按钮样式
80
+ * @property {string} confirmTextClass - 确定按钮类名
81
+ * @property {string} confirmTextStyle - 确定按钮样式
82
+ */
83
+ const props = defineProps({
84
+ customClass: {
85
+ type: String,
86
+ default: "",
87
+ },
88
+ showClose: {
89
+ type: Boolean,
90
+ default: true,
91
+ },
92
+ modelValue: {
93
+ type: Boolean,
94
+ default: false,
95
+ },
96
+ title: {
97
+ type: String,
98
+ default: "提示",
99
+ },
100
+ title_img_url: {
101
+ type: String,
102
+ default: "",
103
+ },
104
+ title_img_style: {
105
+ type: String,
106
+ default: "",
107
+ },
108
+ title_img_class: {
109
+ type: String,
110
+ default: "",
111
+ },
112
+ width: {
113
+ type: Number,
114
+ default: 400,
115
+ },
116
+ center: {
117
+ type: Boolean,
118
+ default: true,
119
+ },
120
+ btnCount: {
121
+ type: Number,
122
+ default: 2,
123
+ },
124
+ confirmText: {
125
+ type: String,
126
+ default: "确定",
127
+ },
128
+ cancelText: {
129
+ type: String,
130
+ default: "关闭",
131
+ },
132
+ cancelTextClass: {
133
+ type: String,
134
+ default: ''
135
+ },
136
+ cancelTextStyle: {
137
+ type: String,
138
+ default: ''
139
+ },
140
+ confirmTextClass: {
141
+ type: String,
142
+ default: ''
143
+ },
144
+ confirmTextStyle: {
145
+ type: String,
146
+ default: ''
147
+ }
148
+ })
149
+
150
+ /**
151
+ * 事件定义
152
+ * @event cancel - 取消事件
153
+ * @event confirm - 确认事件
154
+ * @event update:modelValue - 更新可见性事件(用于 v-model 绑定)
155
+ */
156
+ const emit = defineEmits(['cancel', 'confirm', 'update:modelValue'])
157
+
158
+ /**
159
+ * 弹窗显示状态
160
+ * @type {import('vue').Ref<boolean>}
161
+ */
162
+ const show = ref(false)
163
+
164
+ /**
165
+ * 加载状态
166
+ * @type {import('vue').Ref<boolean>}
167
+ */
168
+ const loading = ref(false)
169
+
170
+ /**
171
+ * 监听modelValue属性变化,同步显示状态
172
+ * modelValue: v-model 默认prop
173
+ */
174
+ watch(() => props.modelValue, (val) => {
175
+ show.value = val
176
+ })
177
+
178
+ /**
179
+ * 右上角关闭按钮触发的关闭方法
180
+ * 只关闭弹窗,不触发 cancel 事件
181
+ */
182
+ const handleClose = () => {
183
+ show.value = false
184
+ emit('update:modelValue', false)
185
+ }
186
+
187
+ /**
188
+ * 取消按钮触发的方法
189
+ * 关闭弹窗并触发 cancel 事件
190
+ */
191
+ const exit = () => {
192
+ show.value = false
193
+ emit('update:modelValue', false)
194
+ emit('cancel')
195
+ }
196
+
197
+ /**
198
+ * 确认按钮触发的方法
199
+ * 触发 confirm 事件(不自动关闭弹窗,由父组件控制)
200
+ */
201
+ const save = () => {
202
+ emit('confirm')
203
+ }
204
+ </script>