vome-core 0.0.99 → 0.0.101
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/admin/config/plugin-dev.js +1 -1
- package/dist/admin/crud/components/vm-color-picker.vue +303 -86
- package/dist/admin/crud/config.js +1 -1
- package/dist/admin/crud/confirm.js +1 -1
- package/dist/admin/crud/dict.js +1 -1
- package/dist/admin/crud/index.js +1 -1
- package/dist/admin/crud/key.js +1 -1
- package/dist/admin/crud/mitt.js +1 -1
- package/dist/admin/crud/plugins.js +1 -1
- package/dist/admin/crud/span.js +1 -1
- package/dist/admin/crud/style.js +1 -1
- package/dist/admin/crud/validate.js +1 -1
- package/dist/admin/directives/perm.js +1 -1
- package/dist/admin/hooks/useUpload.js +1 -1
- package/dist/admin/lib/browser.js +1 -1
- package/dist/admin/lib/cn.js +1 -1
- package/dist/admin/lib/dialog-float.js +1 -1
- package/dist/admin/lib/export-excel.js +1 -1
- package/dist/admin/lib/import-excel.js +1 -1
- package/dist/admin/lib/json.js +1 -1
- package/dist/admin/lib/menu.js +1 -1
- package/dist/admin/lib/tree.js +1 -1
- package/dist/admin/lib/upload.js +1 -1
- package/dist/admin/lib/video-frame.js +1 -1
- package/dist/index.js +1 -1
- package/dist/server/index.js +1 -1
- package/dist/shared/excel.js +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/locale-json.js +1 -1
- package/dist/shared/tree.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const _0x295ee3=_0x594b;(function(_0x54b0a5,_0x9c184a){const _0x56d320=_0x594b,_0x24e3b9=_0x54b0a5();while(!![]){try{const _0x323a0b=-parseInt(_0x56d320(0xff))/0x1+parseInt(_0x56d320(0xf6))/0x2*(parseInt(_0x56d320(0xf9))/0x3)+parseInt(_0x56d320(0xf4))/0x4+parseInt(_0x56d320(0x106))/0x5+-parseInt(_0x56d320(0xf5))/0x6*(parseInt(_0x56d320(0x10e))/0x7)+parseInt(_0x56d320(0xf8))/0x8*(-parseInt(_0x56d320(0x107))/0x9)+-parseInt(_0x56d320(0x110))/0xa*(-parseInt(_0x56d320(0x10d))/0xb);if(_0x323a0b===_0x9c184a)break;else _0x24e3b9['push'](_0x24e3b9['shift']());}catch(_0x108708){_0x24e3b9['push'](_0x24e3b9['shift']());}}}(_0x1bff,0x9cc86));const ORG='vomekj';export function serviceUrlFromDocs(_0x2577b0){const _0x551a2e=_0x594b,_0x2c6bac=new URL(_0x2577b0),_0x7b59b9=_0x2c6bac[_0x551a2e(0xfc)][_0x551a2e(0x100)](/^www\./,'')['replace'](/^service\./,'');return _0x2c6bac[_0x551a2e(0xfe)]+_0x551a2e(0x109)+_0x7b59b9;}export function seatApiBaseFromDocs(_0x34de33){const _0x41d1e5=_0x594b;return serviceUrlFromDocs(_0x34de33)+_0x41d1e5(0xfb);}export function licenseIssueUrlFromDocs(_0x29720f){const _0x59f92b=_0x594b;return serviceUrlFromDocs(_0x29720f)+_0x59f92b(0x111);}function giteeArchive(_0x13730a){const _0x2724f8=_0x594b;return _0x2724f8(0xf7)+ORG+'/'+_0x13730a+_0x2724f8(0xf0);}function _0x1bff(){const _0x5f07f7=['l2fYy2HPDMuVCMvMCY9OzwfKCY9Tyxn0zxiUEMLW','DM9Tzs1WBhvNAw4TzNjVBNq','DM9Tzs1WBhvNAw4TC2vYDMLJzq','mJG0otq1nvDMwLjdBG','mZa1mJaXn0r5t0T1Dq','5A6m5Pw05lIA5yQH5QIH5z2x6isA5OMl5P6277YilNzVBwxVVjRLKkSGC2vYDMvYios4JIb3zwlVViNVViZMNkZLNldLVidLJ5hMIzpLJixLKi7LJ5hLUipJGii','lY9Zzxj2AwnLlG','57QV5zco56UV','yMfJA2vUza','l3bSDwDPBNmVDxbSB2fK','mJjquxHfANe','mtiZodm0owj2wezvqG','zNjVBNrLBMq','nZu1otuZmg5xrfbhEq','l2fWCc9WBhvNAw5tDg9Yzs93ywXSzxqVBgLJzw5Zzs9PC3n1zq','l3jLCg9ZAxrVCNKVyxjJAgL2zs9Tyxn0zxiUEMLW','zNvSBa','6zkP5A2qic8G5PYn5yQH56UV5O+s5lU26isA5OMl5P6277YilNzVBwxVVjRKU4uGC2vYDMvY77Yj77Ym6ycc5zci5lIk5lYG44cb55+T5l+H44cb5PsV5lUy562j6io95yQB5OMP5Bgv44cc','DM9Tzs1WBhvNAw4TzNvSBa','mJi3odq4oezyu1P3sa','ndjJzwDSy0y','mtaYmtj6yMrsuhm','Ahr0Chm6lY9NAxrLzs5JB20V','mJrKAePnzey','mtq3Dvfzuwj3','Ahr0Chm6lY92B21LA2OUy29T','l2fWCc9WBhvNAw5tDg9Yzs9Zzwf0','Ag9ZDg5HBwu','5yMn56UVicSG5zco56UV','ChjVDg9JB2W','mZC0m1zPu3P6vW','CMvWBgfJzq','57QV5yMn56UV','Ahr0Chm6lY9NAxrODwiUy29TlW'];_0x1bff=function(){return _0x5f07f7;};return _0x1bff();}function githubArchive(_0x69973f){const _0x595de4=_0x594b;return _0x595de4(0x102)+ORG+'/'+_0x69973f+_0x595de4(0x103);}function giteeRepo(_0x1257f2){const _0x7ed006=_0x594b;return _0x7ed006(0xf7)+ORG+'/'+_0x1257f2;}function githubRepo(_0x3808b2){const _0x542a09=_0x594b;return _0x542a09(0x102)+ORG+'/'+_0x3808b2;}function _0x594b(_0xa573c2,_0x9e3808){_0xa573c2=_0xa573c2-0xf0;const _0x1bffe9=_0x1bff();let _0x594b39=_0x1bffe9[_0xa573c2];if(_0x594b['zCQQog']===undefined){var _0x144173=function(_0x1d3a22){const _0x4b0e62='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2577b0='',_0x2c6bac='';for(let _0x7b59b9=0x0,_0x34de33,_0x29720f,_0x13730a=0x0;_0x29720f=_0x1d3a22['charAt'](_0x13730a++);~_0x29720f&&(_0x34de33=_0x7b59b9%0x4?_0x34de33*0x40+_0x29720f:_0x29720f,_0x7b59b9++%0x4)?_0x2577b0+=String['fromCharCode'](0xff&_0x34de33>>(-0x2*_0x7b59b9&0x6)):0x0){_0x29720f=_0x4b0e62['indexOf'](_0x29720f);}for(let _0x69973f=0x0,_0x1257f2=_0x2577b0['length'];_0x69973f<_0x1257f2;_0x69973f++){_0x2c6bac+='%'+('00'+_0x2577b0['charCodeAt'](_0x69973f)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x2c6bac);};_0x594b['nyfQgP']=_0x144173,_0x594b['EWicmL']={},_0x594b['zCQQog']=!![];}const _0xee6628=_0x1bffe9[0x0],_0x5ef687=_0xa573c2+_0xee6628,_0x22fd55=_0x594b['EWicmL'][_0x5ef687];return!_0x22fd55?(_0x594b39=_0x594b['nyfQgP'](_0x594b39),_0x594b['EWicmL'][_0x5ef687]=_0x594b39):_0x594b39=_0x22fd55,_0x594b39;}const docsUrl=_0x295ee3(0xfa);export const pluginDev={'docsUrl':docsUrl,'serviceUrl':serviceUrlFromDocs(docsUrl),'uploadUrl':docsUrl+_0x295ee3(0x10c),'mirrors':{'prefer':'gitee','giteeOrg':_0x295ee3(0xf7)+ORG,'githubOrg':_0x295ee3(0x102)+ORG},'scaffolds':{'full':{'key':_0x295ee3(0xf1),'title':_0x295ee3(0xfd),'desc':_0x295ee3(0x108),'repo':_0x295ee3(0xf3),'repoUrl':giteeRepo(_0x295ee3(0xf3)),'githubRepoUrl':githubRepo(_0x295ee3(0xf3)),'downloadUrl':giteeArchive('vome-plugin-full'),'githubDownloadUrl':githubArchive(_0x295ee3(0xf3))},'backend':{'key':_0x295ee3(0x10b),'title':_0x295ee3(0x10a),'desc':_0x295ee3(0xf2),'repo':'vome-plugin-service','repoUrl':giteeRepo(_0x295ee3(0x105)),'githubRepoUrl':githubRepo('vome-plugin-service'),'downloadUrl':giteeArchive(_0x295ee3(0x105)),'githubDownloadUrl':githubArchive(_0x295ee3(0x105))},'frontend':{'key':_0x295ee3(0x10f),'title':_0x295ee3(0x101),'desc':'后台微应用脚手架(.vome:含\x20web/),以\x20wujie\x20挂载独立前端页面。','repo':'vome-plugin-front','repoUrl':giteeRepo(_0x295ee3(0x104)),'githubRepoUrl':githubRepo(_0x295ee3(0x104)),'downloadUrl':giteeArchive(_0x295ee3(0x104)),'githubDownloadUrl':githubArchive(_0x295ee3(0x104))}}};
|
|
@@ -61,55 +61,81 @@
|
|
|
61
61
|
|
|
62
62
|
<div class="vm-color-picker__sliders">
|
|
63
63
|
<button
|
|
64
|
-
v-if="hasEyeDropper"
|
|
65
64
|
type="button"
|
|
66
65
|
class="vm-color-picker__eyedropper"
|
|
67
66
|
title="吸取颜色"
|
|
67
|
+
:disabled="!hasEyeDropper"
|
|
68
68
|
@click="pickEyeDropper"
|
|
69
69
|
>
|
|
70
|
-
<
|
|
70
|
+
<svg
|
|
71
|
+
viewBox="0 0 24 24"
|
|
72
|
+
width="16"
|
|
73
|
+
height="16"
|
|
74
|
+
fill="none"
|
|
75
|
+
stroke="currentColor"
|
|
76
|
+
stroke-width="2"
|
|
77
|
+
stroke-linecap="round"
|
|
78
|
+
stroke-linejoin="round"
|
|
79
|
+
aria-hidden="true"
|
|
80
|
+
>
|
|
81
|
+
<path d="m2 22 1-1h3l9-9" />
|
|
82
|
+
<path d="M3 21v-3l9-9" />
|
|
83
|
+
<path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.2-.2" />
|
|
84
|
+
</svg>
|
|
71
85
|
</button>
|
|
72
86
|
<div class="vm-color-picker__slider-col">
|
|
73
87
|
<div
|
|
74
88
|
ref="hueRef"
|
|
75
|
-
class="vm-color-
|
|
89
|
+
class="vm-color-picker__track"
|
|
76
90
|
@pointerdown="onHuePointer"
|
|
77
91
|
>
|
|
92
|
+
<div class="vm-color-picker__rail vm-color-picker__rail--hue" />
|
|
78
93
|
<span
|
|
79
94
|
class="vm-color-picker__slider-thumb"
|
|
80
|
-
:style="{ left:
|
|
95
|
+
:style="{ left: sliderThumbLeft(hue / 360) }"
|
|
81
96
|
/>
|
|
82
97
|
</div>
|
|
83
98
|
<div
|
|
84
99
|
ref="alphaRef"
|
|
85
|
-
class="vm-color-
|
|
100
|
+
class="vm-color-picker__track"
|
|
86
101
|
@pointerdown="onAlphaPointer"
|
|
87
102
|
>
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
<div class="vm-color-picker__rail vm-color-picker__rail--alpha">
|
|
104
|
+
<span
|
|
105
|
+
class="vm-color-picker__alpha-fill"
|
|
106
|
+
:style="{ background: alphaBg }"
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
92
109
|
<span
|
|
93
110
|
class="vm-color-picker__slider-thumb"
|
|
94
|
-
:style="{ left:
|
|
111
|
+
:style="{ left: sliderThumbLeft(alpha) }"
|
|
95
112
|
/>
|
|
96
113
|
</div>
|
|
97
114
|
</div>
|
|
98
115
|
</div>
|
|
99
116
|
|
|
100
117
|
<div class="vm-color-picker__footer">
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
<select
|
|
119
|
+
class="vm-color-picker__format"
|
|
120
|
+
:value="colorFormat"
|
|
121
|
+
@change="onFormatChange"
|
|
122
|
+
>
|
|
123
|
+
<option
|
|
124
|
+
v-for="opt in FORMAT_OPTS"
|
|
125
|
+
:key="opt.id"
|
|
126
|
+
:value="opt.id"
|
|
127
|
+
>
|
|
128
|
+
{{ opt.label }}
|
|
129
|
+
</option>
|
|
130
|
+
</select>
|
|
105
131
|
<input
|
|
106
132
|
type="text"
|
|
107
|
-
class="vm-color-
|
|
108
|
-
:value="
|
|
133
|
+
class="vm-color-picker__value"
|
|
134
|
+
:value="panelValue"
|
|
109
135
|
spellcheck="false"
|
|
110
136
|
autocomplete="off"
|
|
111
|
-
@input="
|
|
112
|
-
@blur="
|
|
137
|
+
@input="onPanelValueInput"
|
|
138
|
+
@blur="onPanelValueBlur"
|
|
113
139
|
@keydown.esc.stop="closePanel"
|
|
114
140
|
/>
|
|
115
141
|
<input
|
|
@@ -155,6 +181,17 @@ const emit = defineEmits<{
|
|
|
155
181
|
|
|
156
182
|
type Rgba = { r: number; g: number; b: number; a: number }
|
|
157
183
|
type Hsv = { h: number; s: number; v: number }
|
|
184
|
+
type ColorFormat = 'hex' | 'rgb' | 'css' | 'hsl' | 'hsb'
|
|
185
|
+
|
|
186
|
+
const FORMAT_OPTS: { id: ColorFormat; label: string }[] = [
|
|
187
|
+
{ id: 'hex', label: 'Hex' },
|
|
188
|
+
{ id: 'rgb', label: 'RGB' },
|
|
189
|
+
{ id: 'css', label: 'CSS' },
|
|
190
|
+
{ id: 'hsl', label: 'HSL' },
|
|
191
|
+
{ id: 'hsb', label: 'HSB' },
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
const THUMB_PX = 14
|
|
158
195
|
|
|
159
196
|
const rootRef = ref<HTMLElement | null>(null)
|
|
160
197
|
const triggerRef = ref<HTMLElement | null>(null)
|
|
@@ -166,15 +203,16 @@ const alphaRef = ref<HTMLElement | null>(null)
|
|
|
166
203
|
const open = ref(false)
|
|
167
204
|
const suppressOpen = ref(false)
|
|
168
205
|
const panelStyle = ref<Record<string, string>>({})
|
|
206
|
+
const colorFormat = ref<ColorFormat>('hex')
|
|
169
207
|
|
|
170
208
|
const hue = ref(0)
|
|
171
209
|
const sat = ref(0)
|
|
172
210
|
const val = ref(0)
|
|
173
211
|
const alpha = ref(1)
|
|
174
212
|
|
|
175
|
-
/** 行内
|
|
213
|
+
/** 行内 Hex(不含 #);面板按当前格式展示 */
|
|
176
214
|
const inlineHex = ref('')
|
|
177
|
-
const
|
|
215
|
+
const panelValue = ref('')
|
|
178
216
|
const alphaPctText = ref('100%')
|
|
179
217
|
|
|
180
218
|
const hasEyeDropper = ref(false)
|
|
@@ -188,6 +226,21 @@ function clamp(n: number, min: number, max: number) {
|
|
|
188
226
|
return Math.min(max, Math.max(min, n))
|
|
189
227
|
}
|
|
190
228
|
|
|
229
|
+
/** 拇指左缘:0 → 0,1 → 100%-14px,整圆始终在轨道盒内 */
|
|
230
|
+
function sliderThumbLeft(ratio: number) {
|
|
231
|
+
const t = clamp(ratio, 0, 1)
|
|
232
|
+
return `calc(${t} * (100% - ${THUMB_PX}px))`
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** 指针映射:扣除两端半拇指宽度 */
|
|
236
|
+
function sliderRatio(el: HTMLElement, e: PointerEvent) {
|
|
237
|
+
const r = el.getBoundingClientRect()
|
|
238
|
+
const pad = THUMB_PX / 2
|
|
239
|
+
const usable = r.width - THUMB_PX
|
|
240
|
+
if (usable <= 0) return 0
|
|
241
|
+
return clamp((e.clientX - r.left - pad) / usable, 0, 1)
|
|
242
|
+
}
|
|
243
|
+
|
|
191
244
|
function toHex2(n: number) {
|
|
192
245
|
return clamp(Math.round(n), 0, 255).toString(16).padStart(2, '0')
|
|
193
246
|
}
|
|
@@ -251,6 +304,61 @@ function rgbToHsv(r: number, g: number, b: number): Hsv {
|
|
|
251
304
|
return { h, s, v: max }
|
|
252
305
|
}
|
|
253
306
|
|
|
307
|
+
function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } {
|
|
308
|
+
const rr = r / 255
|
|
309
|
+
const gg = g / 255
|
|
310
|
+
const bb = b / 255
|
|
311
|
+
const max = Math.max(rr, gg, bb)
|
|
312
|
+
const min = Math.min(rr, gg, bb)
|
|
313
|
+
const l = (max + min) / 2
|
|
314
|
+
const d = max - min
|
|
315
|
+
let h = 0
|
|
316
|
+
let s = 0
|
|
317
|
+
if (d !== 0) {
|
|
318
|
+
s = d / (1 - Math.abs(2 * l - 1))
|
|
319
|
+
if (max === rr) h = ((gg - bb) / d) % 6
|
|
320
|
+
else if (max === gg) h = (bb - rr) / d + 2
|
|
321
|
+
else h = (rr - gg) / d + 4
|
|
322
|
+
h *= 60
|
|
323
|
+
if (h < 0) h += 360
|
|
324
|
+
}
|
|
325
|
+
return { h, s, l }
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {
|
|
329
|
+
const hh = ((h % 360) + 360) % 360
|
|
330
|
+
const c = (1 - Math.abs(2 * l - 1)) * s
|
|
331
|
+
const x = c * (1 - Math.abs(((hh / 60) % 2) - 1))
|
|
332
|
+
const m = l - c / 2
|
|
333
|
+
let rp = 0
|
|
334
|
+
let gp = 0
|
|
335
|
+
let bp = 0
|
|
336
|
+
if (hh < 60) {
|
|
337
|
+
rp = c
|
|
338
|
+
gp = x
|
|
339
|
+
} else if (hh < 120) {
|
|
340
|
+
rp = x
|
|
341
|
+
gp = c
|
|
342
|
+
} else if (hh < 180) {
|
|
343
|
+
gp = c
|
|
344
|
+
bp = x
|
|
345
|
+
} else if (hh < 240) {
|
|
346
|
+
gp = x
|
|
347
|
+
bp = c
|
|
348
|
+
} else if (hh < 300) {
|
|
349
|
+
rp = x
|
|
350
|
+
bp = c
|
|
351
|
+
} else {
|
|
352
|
+
rp = c
|
|
353
|
+
bp = x
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
r: (rp + m) * 255,
|
|
357
|
+
g: (gp + m) * 255,
|
|
358
|
+
b: (bp + m) * 255,
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
254
362
|
/** 解析为 rgba;无效返回 null */
|
|
255
363
|
function parseColor(raw: string): Rgba | null {
|
|
256
364
|
const s = raw.trim()
|
|
@@ -283,9 +391,8 @@ function parseColor(raw: string): Rgba | null {
|
|
|
283
391
|
return null
|
|
284
392
|
}
|
|
285
393
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
return parseColor(`#${bare}`)
|
|
394
|
+
if (/^[0-9a-fA-F]{3}$/.test(s) || /^[0-9a-fA-F]{6}$/.test(s) || /^[0-9a-fA-F]{8}$/.test(s)) {
|
|
395
|
+
return parseColor(`#${s}`)
|
|
289
396
|
}
|
|
290
397
|
|
|
291
398
|
const m = s.match(
|
|
@@ -299,6 +406,68 @@ function parseColor(raw: string): Rgba | null {
|
|
|
299
406
|
a: m[4] != null ? clamp(Number(m[4]), 0, 1) : 1,
|
|
300
407
|
}
|
|
301
408
|
}
|
|
409
|
+
|
|
410
|
+
const hsl = s.match(
|
|
411
|
+
/^hsla?\(\s*([\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%(?:\s*,\s*([\d.]+))?\s*\)$/i,
|
|
412
|
+
)
|
|
413
|
+
if (hsl) {
|
|
414
|
+
const rgb = hslToRgb(Number(hsl[1]), Number(hsl[2]) / 100, Number(hsl[3]) / 100)
|
|
415
|
+
return {
|
|
416
|
+
r: rgb.r,
|
|
417
|
+
g: rgb.g,
|
|
418
|
+
b: rgb.b,
|
|
419
|
+
a: hsl[4] != null ? clamp(Number(hsl[4]), 0, 1) : 1,
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return null
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** 按当前格式解析面板中间输入(不改变 alpha,除非 CSS/rgba 自带) */
|
|
427
|
+
function parsePanelValue(raw: string, keepAlpha: number): Rgba | null {
|
|
428
|
+
const s = raw.trim()
|
|
429
|
+
if (!s) return null
|
|
430
|
+
const fmt = colorFormat.value
|
|
431
|
+
|
|
432
|
+
if (fmt === 'hex' || fmt === 'css') {
|
|
433
|
+
const parsed = parseColor(s)
|
|
434
|
+
if (!parsed) return null
|
|
435
|
+
if (fmt === 'hex') parsed.a = keepAlpha
|
|
436
|
+
return parsed
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (fmt === 'rgb') {
|
|
440
|
+
const parts = s.split(/[,\s]+/).filter(Boolean)
|
|
441
|
+
if (parts.length < 3) return null
|
|
442
|
+
const r = Number(parts[0])
|
|
443
|
+
const g = Number(parts[1])
|
|
444
|
+
const b = Number(parts[2])
|
|
445
|
+
if (![r, g, b].every(Number.isFinite)) return null
|
|
446
|
+
return { r: clamp(r, 0, 255), g: clamp(g, 0, 255), b: clamp(b, 0, 255), a: keepAlpha }
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (fmt === 'hsl') {
|
|
450
|
+
const parts = s.replace(/%/g, '').split(/[,\s]+/).filter(Boolean)
|
|
451
|
+
if (parts.length < 3) return null
|
|
452
|
+
const h = Number(parts[0])
|
|
453
|
+
const satPct = Number(parts[1])
|
|
454
|
+
const lPct = Number(parts[2])
|
|
455
|
+
if (![h, satPct, lPct].every(Number.isFinite)) return null
|
|
456
|
+
const rgb = hslToRgb(h, clamp(satPct, 0, 100) / 100, clamp(lPct, 0, 100) / 100)
|
|
457
|
+
return { ...rgb, a: keepAlpha }
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (fmt === 'hsb') {
|
|
461
|
+
const parts = s.replace(/%/g, '').split(/[,\s]+/).filter(Boolean)
|
|
462
|
+
if (parts.length < 3) return null
|
|
463
|
+
const h = Number(parts[0])
|
|
464
|
+
const sPct = Number(parts[1])
|
|
465
|
+
const bPct = Number(parts[2])
|
|
466
|
+
if (![h, sPct, bPct].every(Number.isFinite)) return null
|
|
467
|
+
const rgb = hsvToRgb(h, clamp(sPct, 0, 100) / 100, clamp(bPct, 0, 100) / 100)
|
|
468
|
+
return { ...rgb, a: keepAlpha }
|
|
469
|
+
}
|
|
470
|
+
|
|
302
471
|
return null
|
|
303
472
|
}
|
|
304
473
|
|
|
@@ -311,6 +480,31 @@ function currentHex(): string {
|
|
|
311
480
|
return rgbToHex(r, g, b, alpha.value)
|
|
312
481
|
}
|
|
313
482
|
|
|
483
|
+
function formatPanelValue(): string {
|
|
484
|
+
const { r, g, b } = currentRgb()
|
|
485
|
+
const rr = Math.round(r)
|
|
486
|
+
const gg = Math.round(g)
|
|
487
|
+
const bb = Math.round(b)
|
|
488
|
+
const a = alpha.value
|
|
489
|
+
switch (colorFormat.value) {
|
|
490
|
+
case 'rgb':
|
|
491
|
+
return `${rr}, ${gg}, ${bb}`
|
|
492
|
+
case 'css':
|
|
493
|
+
return a < 1 - 1e-6
|
|
494
|
+
? `rgba(${rr}, ${gg}, ${bb}, ${Number(a.toFixed(2))})`
|
|
495
|
+
: `#${toHex2(rr)}${toHex2(gg)}${toHex2(bb)}`
|
|
496
|
+
case 'hsl': {
|
|
497
|
+
const hsl = rgbToHsl(rr, gg, bb)
|
|
498
|
+
return `${Math.round(hsl.h)}, ${Math.round(hsl.s * 100)}%, ${Math.round(hsl.l * 100)}%`
|
|
499
|
+
}
|
|
500
|
+
case 'hsb':
|
|
501
|
+
return `${Math.round(hue.value)}, ${Math.round(sat.value * 100)}%, ${Math.round(val.value * 100)}%`
|
|
502
|
+
case 'hex':
|
|
503
|
+
default:
|
|
504
|
+
return `${toHex2(rr)}${toHex2(gg)}${toHex2(bb)}`
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
314
508
|
const cssColor = computed(() => {
|
|
315
509
|
const parsed = parseColor(props.modelValue ?? '')
|
|
316
510
|
if (!parsed && !(props.modelValue ?? '').trim()) return 'transparent'
|
|
@@ -338,9 +532,8 @@ const svCursorStyle = computed(() => ({
|
|
|
338
532
|
function syncTextsFromState() {
|
|
339
533
|
const hex = currentHex()
|
|
340
534
|
const bare = hex.startsWith('#') ? hex.slice(1) : hex
|
|
341
|
-
// 行内只展示 RGB 六位;透明度在面板 % 里改
|
|
342
535
|
inlineHex.value = bare.slice(0, 6)
|
|
343
|
-
|
|
536
|
+
panelValue.value = formatPanelValue()
|
|
344
537
|
alphaPctText.value = `${Math.round(alpha.value * 100)}%`
|
|
345
538
|
}
|
|
346
539
|
|
|
@@ -362,7 +555,7 @@ function loadFromModel(raw: string | null | undefined) {
|
|
|
362
555
|
val.value = 0
|
|
363
556
|
alpha.value = 1
|
|
364
557
|
inlineHex.value = ''
|
|
365
|
-
|
|
558
|
+
panelValue.value = ''
|
|
366
559
|
alphaPctText.value = '100%'
|
|
367
560
|
return
|
|
368
561
|
}
|
|
@@ -383,7 +576,6 @@ watch(
|
|
|
383
576
|
() => props.modelValue,
|
|
384
577
|
(v) => {
|
|
385
578
|
if (open.value) {
|
|
386
|
-
// 面板打开时若外部写入且与当前一致则跳过抖动
|
|
387
579
|
const cur = currentHex()
|
|
388
580
|
const parsed = parseColor(v ?? '')
|
|
389
581
|
if (parsed && rgbToHex(parsed.r, parsed.g, parsed.b, parsed.a) === cur) return
|
|
@@ -413,7 +605,7 @@ function onInlineInput(e: Event) {
|
|
|
413
605
|
hue.value = hsv.h
|
|
414
606
|
sat.value = hsv.s
|
|
415
607
|
val.value = hsv.v
|
|
416
|
-
|
|
608
|
+
panelValue.value = formatPanelValue()
|
|
417
609
|
commit(currentHex())
|
|
418
610
|
} else {
|
|
419
611
|
const raw = inlineHex.value.trim()
|
|
@@ -437,25 +629,25 @@ function onInlineBlur() {
|
|
|
437
629
|
}
|
|
438
630
|
}
|
|
439
631
|
|
|
440
|
-
function
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
632
|
+
function onPanelValueInput(e: Event) {
|
|
633
|
+
panelValue.value = (e.target as HTMLInputElement).value
|
|
634
|
+
const parsed = parsePanelValue(panelValue.value, alpha.value)
|
|
635
|
+
if (!parsed) return
|
|
636
|
+
const hsv = rgbToHsv(parsed.r, parsed.g, parsed.b)
|
|
637
|
+
hue.value = hsv.h
|
|
638
|
+
sat.value = hsv.s
|
|
639
|
+
val.value = hsv.v
|
|
640
|
+
if (colorFormat.value === 'css') alpha.value = parsed.a
|
|
641
|
+
const hex = currentHex()
|
|
642
|
+
inlineHex.value = hex.replace(/^#/, '').slice(0, 6)
|
|
643
|
+
alphaPctText.value = `${Math.round(alpha.value * 100)}%`
|
|
644
|
+
commit(hex)
|
|
453
645
|
}
|
|
454
646
|
|
|
455
|
-
function
|
|
456
|
-
const parsed =
|
|
647
|
+
function onPanelValueBlur() {
|
|
648
|
+
const parsed = parsePanelValue(panelValue.value, alpha.value)
|
|
457
649
|
if (parsed) {
|
|
458
|
-
parsed.a = alpha.value
|
|
650
|
+
if (colorFormat.value !== 'css') parsed.a = alpha.value
|
|
459
651
|
applyRgba(parsed, true)
|
|
460
652
|
} else {
|
|
461
653
|
syncTextsFromState()
|
|
@@ -479,6 +671,12 @@ function onAlphaPctBlur() {
|
|
|
479
671
|
commit(currentHex())
|
|
480
672
|
}
|
|
481
673
|
|
|
674
|
+
function onFormatChange(e: Event) {
|
|
675
|
+
const v = (e.target as HTMLSelectElement).value as ColorFormat
|
|
676
|
+
colorFormat.value = v
|
|
677
|
+
syncTextsFromState()
|
|
678
|
+
}
|
|
679
|
+
|
|
482
680
|
function ratioFromEvent(el: HTMLElement, e: PointerEvent, axis: 'x' | 'y' | 'both') {
|
|
483
681
|
const r = el.getBoundingClientRect()
|
|
484
682
|
const x = clamp((e.clientX - r.left) / r.width, 0, 1)
|
|
@@ -523,8 +721,7 @@ function onHuePointer(e: PointerEvent) {
|
|
|
523
721
|
const el = hueRef.value
|
|
524
722
|
if (!el) return
|
|
525
723
|
bindDrag(el, e, (ev) => {
|
|
526
|
-
|
|
527
|
-
hue.value = x * 360
|
|
724
|
+
hue.value = sliderRatio(el, ev) * 360
|
|
528
725
|
commitFromState()
|
|
529
726
|
})
|
|
530
727
|
}
|
|
@@ -533,14 +730,17 @@ function onAlphaPointer(e: PointerEvent) {
|
|
|
533
730
|
const el = alphaRef.value
|
|
534
731
|
if (!el) return
|
|
535
732
|
bindDrag(el, e, (ev) => {
|
|
536
|
-
|
|
537
|
-
alpha.value = x
|
|
733
|
+
alpha.value = sliderRatio(el, ev)
|
|
538
734
|
commitFromState()
|
|
539
735
|
})
|
|
540
736
|
}
|
|
541
737
|
|
|
542
738
|
async function pickEyeDropper() {
|
|
543
|
-
const ED = (
|
|
739
|
+
const ED = (
|
|
740
|
+
window as unknown as {
|
|
741
|
+
EyeDropper?: new () => { open: () => Promise<{ sRGBHex: string }> }
|
|
742
|
+
}
|
|
743
|
+
).EyeDropper
|
|
544
744
|
if (!ED) return
|
|
545
745
|
try {
|
|
546
746
|
const result = await new ED().open()
|
|
@@ -564,7 +764,7 @@ function syncPanelPos() {
|
|
|
564
764
|
left = Math.max(8, window.innerWidth - width - 8)
|
|
565
765
|
}
|
|
566
766
|
let top = r.bottom + 6
|
|
567
|
-
const estH =
|
|
767
|
+
const estH = 320
|
|
568
768
|
if (top + estH > window.innerHeight - 8) {
|
|
569
769
|
top = Math.max(8, r.top - estH - 6)
|
|
570
770
|
}
|
|
@@ -623,9 +823,7 @@ function onWinChange() {
|
|
|
623
823
|
}
|
|
624
824
|
|
|
625
825
|
onMounted(() => {
|
|
626
|
-
hasEyeDropper.value =
|
|
627
|
-
typeof window !== 'undefined' &&
|
|
628
|
-
'EyeDropper' in window
|
|
826
|
+
hasEyeDropper.value = typeof window !== 'undefined' && 'EyeDropper' in window
|
|
629
827
|
document.addEventListener('pointerdown', onDocPointer, true)
|
|
630
828
|
window.addEventListener('resize', onWinChange)
|
|
631
829
|
window.addEventListener('scroll', onWinChange, true)
|
|
@@ -741,6 +939,7 @@ onUnmounted(() => {
|
|
|
741
939
|
0 4px 6px -1px rgb(0 0 0 / 10%),
|
|
742
940
|
0 10px 24px -4px rgb(0 0 0 / 12%);
|
|
743
941
|
user-select: none;
|
|
942
|
+
overflow: visible;
|
|
744
943
|
}
|
|
745
944
|
|
|
746
945
|
.vm-color-picker__sv {
|
|
@@ -779,17 +978,26 @@ onUnmounted(() => {
|
|
|
779
978
|
width: 28px;
|
|
780
979
|
height: 28px;
|
|
781
980
|
padding: 0;
|
|
782
|
-
border: 1px solid var(--border);
|
|
981
|
+
border: 1px solid var(--border, #e5e5e5);
|
|
783
982
|
border-radius: 6px;
|
|
784
|
-
background:
|
|
785
|
-
color:
|
|
786
|
-
font-size: 15px;
|
|
983
|
+
background: #fff;
|
|
984
|
+
color: #111;
|
|
787
985
|
cursor: pointer;
|
|
788
986
|
|
|
789
|
-
&:hover {
|
|
987
|
+
&:hover:not(:disabled) {
|
|
790
988
|
border-color: var(--brand, #4e5dff);
|
|
791
989
|
color: var(--brand, #4e5dff);
|
|
792
990
|
}
|
|
991
|
+
|
|
992
|
+
&:disabled {
|
|
993
|
+
opacity: 0.35;
|
|
994
|
+
cursor: not-allowed;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
svg {
|
|
998
|
+
display: block;
|
|
999
|
+
flex-shrink: 0;
|
|
1000
|
+
}
|
|
793
1001
|
}
|
|
794
1002
|
|
|
795
1003
|
.vm-color-picker__slider-col {
|
|
@@ -797,19 +1005,30 @@ onUnmounted(() => {
|
|
|
797
1005
|
min-width: 0;
|
|
798
1006
|
display: flex;
|
|
799
1007
|
flex-direction: column;
|
|
800
|
-
gap:
|
|
1008
|
+
gap: 10px;
|
|
1009
|
+
overflow: visible;
|
|
801
1010
|
}
|
|
802
1011
|
|
|
803
|
-
.vm-color-
|
|
804
|
-
.vm-color-picker__alpha {
|
|
1012
|
+
.vm-color-picker__track {
|
|
805
1013
|
position: relative;
|
|
806
|
-
|
|
807
|
-
|
|
1014
|
+
width: 100%;
|
|
1015
|
+
height: 14px;
|
|
1016
|
+
overflow: visible;
|
|
808
1017
|
cursor: pointer;
|
|
809
1018
|
touch-action: none;
|
|
810
1019
|
}
|
|
811
1020
|
|
|
812
|
-
.vm-color-
|
|
1021
|
+
.vm-color-picker__rail {
|
|
1022
|
+
position: absolute;
|
|
1023
|
+
left: 7px;
|
|
1024
|
+
right: 7px;
|
|
1025
|
+
top: 1px;
|
|
1026
|
+
height: 12px;
|
|
1027
|
+
border-radius: 999px;
|
|
1028
|
+
pointer-events: none;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
.vm-color-picker__rail--hue {
|
|
813
1032
|
background: linear-gradient(
|
|
814
1033
|
to right,
|
|
815
1034
|
#f00 0%,
|
|
@@ -822,7 +1041,7 @@ onUnmounted(() => {
|
|
|
822
1041
|
);
|
|
823
1042
|
}
|
|
824
1043
|
|
|
825
|
-
.vm-color-
|
|
1044
|
+
.vm-color-picker__rail--alpha {
|
|
826
1045
|
background-image:
|
|
827
1046
|
linear-gradient(45deg, #c8c8c8 25%, transparent 25%),
|
|
828
1047
|
linear-gradient(-45deg, #c8c8c8 25%, transparent 25%),
|
|
@@ -839,23 +1058,23 @@ onUnmounted(() => {
|
|
|
839
1058
|
}
|
|
840
1059
|
|
|
841
1060
|
.vm-color-picker__alpha-fill {
|
|
842
|
-
|
|
843
|
-
|
|
1061
|
+
display: block;
|
|
1062
|
+
width: 100%;
|
|
1063
|
+
height: 100%;
|
|
844
1064
|
border-radius: inherit;
|
|
845
|
-
pointer-events: none;
|
|
846
1065
|
}
|
|
847
1066
|
|
|
848
1067
|
.vm-color-picker__slider-thumb {
|
|
849
1068
|
position: absolute;
|
|
850
|
-
top:
|
|
1069
|
+
top: 0;
|
|
851
1070
|
width: 14px;
|
|
852
1071
|
height: 14px;
|
|
853
|
-
margin: -7px 0 0 -7px;
|
|
854
1072
|
border: 2px solid #fff;
|
|
855
1073
|
border-radius: 50%;
|
|
856
1074
|
background: transparent;
|
|
857
1075
|
box-shadow: 0 0 0 1px rgb(0 0 0 / 35%);
|
|
858
1076
|
pointer-events: none;
|
|
1077
|
+
box-sizing: border-box;
|
|
859
1078
|
}
|
|
860
1079
|
|
|
861
1080
|
.vm-color-picker__footer {
|
|
@@ -867,33 +1086,31 @@ onUnmounted(() => {
|
|
|
867
1086
|
|
|
868
1087
|
.vm-color-picker__format {
|
|
869
1088
|
flex-shrink: 0;
|
|
870
|
-
display: inline-flex;
|
|
871
|
-
align-items: center;
|
|
872
|
-
gap: 2px;
|
|
873
1089
|
height: 28px;
|
|
1090
|
+
max-width: 72px;
|
|
874
1091
|
padding: 0 6px;
|
|
875
|
-
border: 1px solid var(--border);
|
|
1092
|
+
border: 1px solid var(--border, #e5e5e5);
|
|
876
1093
|
border-radius: 6px;
|
|
877
|
-
background: var(--muted);
|
|
878
|
-
color: var(--
|
|
1094
|
+
background: var(--muted, #f5f5f5);
|
|
1095
|
+
color: var(--foreground, #111);
|
|
879
1096
|
font-size: 12px;
|
|
880
|
-
|
|
881
|
-
|
|
1097
|
+
cursor: pointer;
|
|
1098
|
+
appearance: auto;
|
|
882
1099
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1100
|
+
&:focus {
|
|
1101
|
+
border-color: var(--brand, #4e5dff);
|
|
1102
|
+
outline: none;
|
|
886
1103
|
}
|
|
887
1104
|
}
|
|
888
1105
|
|
|
889
|
-
.vm-color-
|
|
1106
|
+
.vm-color-picker__value,
|
|
890
1107
|
.vm-color-picker__alpha-pct {
|
|
891
1108
|
height: 28px;
|
|
892
1109
|
padding: 0 8px;
|
|
893
|
-
border: 1px solid var(--border);
|
|
1110
|
+
border: 1px solid var(--border, #e5e5e5);
|
|
894
1111
|
border-radius: 6px;
|
|
895
|
-
background: var(--muted);
|
|
896
|
-
color: var(--foreground);
|
|
1112
|
+
background: var(--muted, #f5f5f5);
|
|
1113
|
+
color: var(--foreground, #111);
|
|
897
1114
|
font-size: 12px;
|
|
898
1115
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
899
1116
|
outline: none;
|
|
@@ -903,7 +1120,7 @@ onUnmounted(() => {
|
|
|
903
1120
|
}
|
|
904
1121
|
}
|
|
905
1122
|
|
|
906
|
-
.vm-color-
|
|
1123
|
+
.vm-color-picker__value {
|
|
907
1124
|
flex: 1;
|
|
908
1125
|
min-width: 0;
|
|
909
1126
|
}
|