@scx-js/scx-ui 0.0.1
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/index.js +44 -0
- package/package.json +29 -0
- package/scx-context-menu/index.css +51 -0
- package/scx-context-menu/index.js +65 -0
- package/scx-context-menu/index.vue +86 -0
- package/scx-drag/index.js +30 -0
- package/scx-group/index.css +66 -0
- package/scx-group/index.vue +154 -0
- package/scx-icon/index.css +4 -0
- package/scx-icon/index.vue +20 -0
- package/scx-input/index.css +39 -0
- package/scx-input/index.vue +44 -0
- package/scx-panel/index.css +47 -0
- package/scx-panel/index.vue +28 -0
- package/scx-panel/scx-panel-item.css +36 -0
- package/scx-panel/scx-panel-item.vue +22 -0
- package/scx-progress/index.css +33 -0
- package/scx-progress/index.vue +29 -0
- package/scx-switch/index.css +109 -0
- package/scx-switch/index.vue +31 -0
- package/scx-upload/helper.js +177 -0
- package/scx-upload/index.css +204 -0
- package/scx-upload/index.vue +235 -0
- package/scx-upload-list/index.css +89 -0
- package/scx-upload-list/index.vue +228 -0
- package/style/changeTheme.js +13 -0
- package/style/dark.css +59 -0
- package/style/default.css +98 -0
- package/style/normalize.css +35 -0
package/index.js
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
export * from "./scx-context-menu/index.js";
|
2
|
+
export * from "./scx-drag/index.js";
|
3
|
+
|
4
|
+
import ScxGroup from "./scx-group/index.vue";
|
5
|
+
import ScxIcon from "./scx-icon/index.vue";
|
6
|
+
import ScxInput from "./scx-input/index.vue";
|
7
|
+
import ScxPanel from "./scx-panel/index.vue";
|
8
|
+
import ScxPanelItem from "./scx-panel/scx-panel-item.vue";
|
9
|
+
import ScxProgress from "./scx-progress/index.vue";
|
10
|
+
import ScxSwitch from "./scx-switch/index.vue";
|
11
|
+
import ScxUpload from "./scx-upload/index.vue";
|
12
|
+
import ScxUploadList from "./scx-upload-list/index.vue";
|
13
|
+
import {ScxContextMenuDirective} from "./scx-context-menu/index.js";
|
14
|
+
import {ScxDragDirective} from "./scx-drag/index.js";
|
15
|
+
|
16
|
+
//以下为组件
|
17
|
+
const components = [ScxGroup, ScxIcon, ScxInput, ScxPanel, ScxPanelItem, ScxProgress, ScxSwitch, ScxUpload, ScxUploadList];
|
18
|
+
|
19
|
+
//以下为指令
|
20
|
+
const directives = [ScxContextMenuDirective, ScxDragDirective];
|
21
|
+
|
22
|
+
const ScxComponent = {
|
23
|
+
install: (app) => {
|
24
|
+
//安装组件
|
25
|
+
components.forEach(c => app.component(c.name, c));
|
26
|
+
//安装指令
|
27
|
+
directives.forEach(d => app.directive(d.name, d));
|
28
|
+
},
|
29
|
+
};
|
30
|
+
|
31
|
+
export {
|
32
|
+
ScxGroup,
|
33
|
+
ScxIcon,
|
34
|
+
ScxInput,
|
35
|
+
ScxPanel,
|
36
|
+
ScxPanelItem,
|
37
|
+
ScxProgress,
|
38
|
+
ScxSwitch,
|
39
|
+
ScxUpload,
|
40
|
+
ScxUploadList,
|
41
|
+
ScxContextMenuDirective,
|
42
|
+
ScxDragDirective,
|
43
|
+
ScxComponent,
|
44
|
+
};
|
package/package.json
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
{
|
2
|
+
"name": "@scx-js/scx-ui",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"description": "SCX UI",
|
5
|
+
"license": "MIT",
|
6
|
+
"author": "scx567888",
|
7
|
+
"main": "index.js",
|
8
|
+
"type": "module",
|
9
|
+
"scripts": {
|
10
|
+
"dev:test": "vite _test --host",
|
11
|
+
"build:test": "vite build _test",
|
12
|
+
"preview:test": "vite preview _test --host"
|
13
|
+
},
|
14
|
+
"repository": {
|
15
|
+
"type": "git",
|
16
|
+
"url": "https://github.com/scx567888/scx-js.git"
|
17
|
+
},
|
18
|
+
"dependencies": {
|
19
|
+
"@scx-js/scx-common": "0.0.1",
|
20
|
+
"@scx-js/scx-app-x": "0.0.1",
|
21
|
+
"@scx-js/scx-dom": "0.0.1"
|
22
|
+
},
|
23
|
+
"devDependencies": {
|
24
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
25
|
+
"clipboard": "^2.0.0",
|
26
|
+
"vite": "^6.0.0",
|
27
|
+
"vue-router": "^4.0.0"
|
28
|
+
}
|
29
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
/*全局右键菜单*/
|
2
|
+
.scx-context-menu {
|
3
|
+
margin: 0;
|
4
|
+
background: var(--scx-glass-bg);
|
5
|
+
z-index: 3000;
|
6
|
+
position: absolute;
|
7
|
+
list-style-type: none;
|
8
|
+
padding: 3px;
|
9
|
+
border-radius: 2px;
|
10
|
+
font-size: 12px;
|
11
|
+
font-weight: 400;
|
12
|
+
box-shadow: var(--scx-box-shadow);
|
13
|
+
max-height: 500px;
|
14
|
+
overflow: auto;
|
15
|
+
backdrop-filter: var(--scx-glass-bg-filter);
|
16
|
+
box-sizing: border-box;
|
17
|
+
width: max-content;
|
18
|
+
}
|
19
|
+
|
20
|
+
@keyframes from-top {
|
21
|
+
0% {
|
22
|
+
opacity: 0;
|
23
|
+
transform: translateY(-10px);
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
@keyframes from-bottom {
|
28
|
+
0% {
|
29
|
+
opacity: 0;
|
30
|
+
transform: translateY(10px);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
.scx-context-menu.top {
|
35
|
+
animation: from-top 200ms cubic-bezier(.23, 1, .32, 1);
|
36
|
+
}
|
37
|
+
|
38
|
+
.scx-context-menu.bottom {
|
39
|
+
animation: from-bottom 200ms cubic-bezier(.23, 1, .32, 1);
|
40
|
+
}
|
41
|
+
|
42
|
+
.scx-context-menu-item {
|
43
|
+
margin: 0;
|
44
|
+
padding: 7px 16px;
|
45
|
+
cursor: pointer;
|
46
|
+
}
|
47
|
+
|
48
|
+
.scx-context-menu-item:hover {
|
49
|
+
background: var(--scx-theme);
|
50
|
+
color: var(--scx-bg);
|
51
|
+
}
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import ScxContextMenu from "./index.vue";
|
2
|
+
import {h, render} from "vue";
|
3
|
+
|
4
|
+
// 这里因为 vite 每次导入时都使用不同的 上下文导致 contextMenuInstance 为空 这里直接绑定到 window 上
|
5
|
+
// let contextMenuInstance;
|
6
|
+
|
7
|
+
function getInstance() {
|
8
|
+
return window["__SCX_CONTEXT_MENU_INSTANCE__"];
|
9
|
+
}
|
10
|
+
|
11
|
+
function setInstance(instance) {
|
12
|
+
return window["__SCX_CONTEXT_MENU_INSTANCE__"] = instance;
|
13
|
+
}
|
14
|
+
|
15
|
+
function bodyClick(e) {
|
16
|
+
let isOnContextmenu = e.target.closest(".scx-context-menu");
|
17
|
+
|
18
|
+
if (!isOnContextmenu && getInstance()) {
|
19
|
+
closeContextMenu();
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
function showContextMenu(e, value) {
|
24
|
+
//默认阻止事件冒泡
|
25
|
+
e.stopPropagation();
|
26
|
+
const instance = getInstance();
|
27
|
+
if (instance) {
|
28
|
+
document.body.removeChild(instance);
|
29
|
+
}
|
30
|
+
const container = document.createElement("div");
|
31
|
+
let vm = h(ScxContextMenu, {
|
32
|
+
mouseEvent: e,
|
33
|
+
contextMenuItems: value,
|
34
|
+
});
|
35
|
+
render(vm, container);
|
36
|
+
setInstance(container);
|
37
|
+
document.body.appendChild(container);
|
38
|
+
document.body.addEventListener("click", bodyClick);
|
39
|
+
}
|
40
|
+
|
41
|
+
function closeContextMenu() {
|
42
|
+
document.body.removeChild(getInstance());
|
43
|
+
setInstance(null);
|
44
|
+
document.body.removeEventListener("click", bodyClick);
|
45
|
+
}
|
46
|
+
|
47
|
+
const ScxContextMenuDirective = {
|
48
|
+
name: "contextmenu",
|
49
|
+
//待处理
|
50
|
+
mounted(el, {value}) {
|
51
|
+
el.oncontextmenu = (e) => {
|
52
|
+
showContextMenu(e, value);
|
53
|
+
return false;
|
54
|
+
};
|
55
|
+
},
|
56
|
+
updated(el, {value}) {
|
57
|
+
|
58
|
+
},
|
59
|
+
};
|
60
|
+
|
61
|
+
export {
|
62
|
+
ScxContextMenuDirective,
|
63
|
+
showContextMenu,
|
64
|
+
closeContextMenu,
|
65
|
+
};
|
@@ -0,0 +1,86 @@
|
|
1
|
+
<template>
|
2
|
+
<div ref="scxContextMenuRef" class="scx-context-menu" @contextmenu.prevent="">
|
3
|
+
<div v-for="item in contextMenuItems" class="scx-context-menu-item" @click="callItemCallBack(item)">
|
4
|
+
<component :is="renderLabel(item)"></component>
|
5
|
+
</div>
|
6
|
+
</div>
|
7
|
+
</template>
|
8
|
+
|
9
|
+
<script>
|
10
|
+
//todo 多级菜单 ?
|
11
|
+
import "./index.css";
|
12
|
+
import {nextTick, onMounted, ref} from "vue";
|
13
|
+
import {closeContextMenu} from "./index.js";
|
14
|
+
import {isFunction} from "../../scx-common/index.js";
|
15
|
+
|
16
|
+
export default {
|
17
|
+
name: "scx-context-menu",
|
18
|
+
props: {
|
19
|
+
mouseEvent: MouseEvent,
|
20
|
+
contextMenuItems: Array
|
21
|
+
},
|
22
|
+
setup(props, context) {
|
23
|
+
const scxContextMenuRef = ref(null);
|
24
|
+
|
25
|
+
//设置初始状态 保证元素可以正确的获取的内容大小以便后续计算
|
26
|
+
function initStatus() {
|
27
|
+
scxContextMenuRef.value.style.top = 0;
|
28
|
+
scxContextMenuRef.value.style.left = 0;
|
29
|
+
scxContextMenuRef.value.style.visibility = "hidden";
|
30
|
+
}
|
31
|
+
|
32
|
+
function setStatus(top, left, type) {
|
33
|
+
scxContextMenuRef.value.classList.add(type);
|
34
|
+
scxContextMenuRef.value.style.top = top + "px";
|
35
|
+
scxContextMenuRef.value.style.left = left + "px";
|
36
|
+
scxContextMenuRef.value.style.visibility = "unset";
|
37
|
+
}
|
38
|
+
|
39
|
+
function show(mouseEvent) {
|
40
|
+
initStatus();
|
41
|
+
//todo 或者用 clientX ?
|
42
|
+
let x = mouseEvent.pageX;
|
43
|
+
let y = mouseEvent.pageY;
|
44
|
+
const clientWidth = window.innerWidth;
|
45
|
+
const clientHeight = window.innerHeight;
|
46
|
+
nextTick(() => {
|
47
|
+
const {
|
48
|
+
width,
|
49
|
+
height
|
50
|
+
} = scxContextMenuRef.value.getBoundingClientRect();
|
51
|
+
//todo 这里还可以做优化 以保证 菜单展示不下的时候出现滚动条 通过设置 height 和 width 实现
|
52
|
+
const top = clientHeight > height + y ? y : y - height;
|
53
|
+
const type = clientHeight > height + y ? "top" : "bottom";
|
54
|
+
const left = clientWidth > width + x ? x : x - width;
|
55
|
+
setStatus(top, left, type);
|
56
|
+
});
|
57
|
+
}
|
58
|
+
|
59
|
+
function callItemCallBack(item) {
|
60
|
+
if (item.callback) {
|
61
|
+
item.callback(closeContextMenu);
|
62
|
+
} else {
|
63
|
+
closeContextMenu();
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
function renderLabel(c) {
|
68
|
+
const {label} = c;
|
69
|
+
if (isFunction(label)) {
|
70
|
+
return label;
|
71
|
+
} else {
|
72
|
+
return () => label;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
onMounted(() => show(props.mouseEvent));
|
77
|
+
|
78
|
+
return {
|
79
|
+
scxContextMenuRef,
|
80
|
+
callItemCallBack,
|
81
|
+
renderLabel
|
82
|
+
};
|
83
|
+
}
|
84
|
+
|
85
|
+
};
|
86
|
+
</script>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import {onBeforeUnmount} from "vue";
|
2
|
+
import {ScxDrag} from "@scx-js/scx-dom";
|
3
|
+
|
4
|
+
/**
|
5
|
+
*
|
6
|
+
* @param targetElement
|
7
|
+
* @param options {{dragElement, callback, bounds}}}
|
8
|
+
* @return {ScxDrag}
|
9
|
+
*/
|
10
|
+
function useScxDrag(targetElement, options = {}) {
|
11
|
+
const scxDrag = new ScxDrag(targetElement, options);
|
12
|
+
scxDrag.enable();
|
13
|
+
onBeforeUnmount(() => scxDrag.disable());
|
14
|
+
return scxDrag;
|
15
|
+
}
|
16
|
+
|
17
|
+
const ScxDragDirective = {
|
18
|
+
name: "drag",
|
19
|
+
mounted(el, {value}) {
|
20
|
+
useScxDrag(el, value);
|
21
|
+
},
|
22
|
+
updated(el, {value}) {
|
23
|
+
|
24
|
+
},
|
25
|
+
};
|
26
|
+
|
27
|
+
export {
|
28
|
+
useScxDrag,
|
29
|
+
ScxDragDirective,
|
30
|
+
};
|
@@ -0,0 +1,66 @@
|
|
1
|
+
.scx-group {
|
2
|
+
position: relative;
|
3
|
+
width: 100%;
|
4
|
+
box-sizing: border-box;
|
5
|
+
padding: 10px;
|
6
|
+
/* 特有 css */
|
7
|
+
display: flex;
|
8
|
+
flex-direction: column;
|
9
|
+
row-gap: 10px;
|
10
|
+
align-items: center;
|
11
|
+
}
|
12
|
+
|
13
|
+
.scx-group-item {
|
14
|
+
position: relative;
|
15
|
+
width: 100%;
|
16
|
+
box-sizing: border-box;
|
17
|
+
padding: 10px;
|
18
|
+
/* 特有 css */
|
19
|
+
border: 1px dashed var(--scx-text-placeholder-color);
|
20
|
+
background-color: var(--scx-theme-bg);
|
21
|
+
transition: transform 600ms ease, opacity 600ms ease;
|
22
|
+
}
|
23
|
+
|
24
|
+
.scx-group-item-operation {
|
25
|
+
position: absolute;
|
26
|
+
top: 0;
|
27
|
+
right: 0;
|
28
|
+
display: flex;
|
29
|
+
}
|
30
|
+
|
31
|
+
.scx-group-item-move-up-button {
|
32
|
+
/* 占位用 方便开发者覆盖 css */
|
33
|
+
}
|
34
|
+
|
35
|
+
.scx-group-item-move-down-button {
|
36
|
+
/* 占位用 方便开发者覆盖 css */
|
37
|
+
}
|
38
|
+
|
39
|
+
.scx-group-item-remove-button {
|
40
|
+
/* 占位用 方便开发者覆盖 css */
|
41
|
+
}
|
42
|
+
|
43
|
+
/*简单设置一下 默认占位按钮的样式*/
|
44
|
+
.scx-group-item-operation .placeholder-button {
|
45
|
+
display: flex;
|
46
|
+
height: 22px;
|
47
|
+
width: 22px;
|
48
|
+
justify-content: center;
|
49
|
+
align-items: center;
|
50
|
+
}
|
51
|
+
|
52
|
+
/*以下为 vue 动画*/
|
53
|
+
.scx-group-list-enter-from {
|
54
|
+
opacity: 0;
|
55
|
+
transform: scale(0.98);
|
56
|
+
}
|
57
|
+
|
58
|
+
.scx-group-list-leave-to {
|
59
|
+
opacity: 0;
|
60
|
+
transform: scale(0.98);
|
61
|
+
}
|
62
|
+
|
63
|
+
/* 重要 !!!!! 避免嵌套动画延迟 */
|
64
|
+
.scx-group-list-move .scx-group-list-move {
|
65
|
+
transition: unset;
|
66
|
+
}
|
@@ -0,0 +1,154 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="scx-group">
|
3
|
+
<slot name="before"></slot>
|
4
|
+
<transition-group name="scx-group-list" @before-leave="fixedElement">
|
5
|
+
<div v-for="(item,i) in list" :key="item" class="scx-group-item">
|
6
|
+
<slot :index="i" :item="item"></slot>
|
7
|
+
<div class="scx-group-item-operation">
|
8
|
+
<slot :index="i" :item="item" name="itemOperation"></slot>
|
9
|
+
<div v-if="showMoveUp(i)" class="scx-group-item-move-up-button" @click="groupItemMoveUp(i)">
|
10
|
+
<slot name="moveUpButton">
|
11
|
+
<button class="placeholder-button" type="button">↑</button>
|
12
|
+
</slot>
|
13
|
+
</div>
|
14
|
+
<div v-if="showMoveDown(i)" class="scx-group-item-move-down-button" @click="groupItemMoveDown(i)">
|
15
|
+
<slot name="moveDownButton">
|
16
|
+
<button class="placeholder-button" type="button">↓</button>
|
17
|
+
</slot>
|
18
|
+
</div>
|
19
|
+
<div v-if="showRemoveButton" class="scx-group-item-remove-button" @click="groupItemRemove(i)">
|
20
|
+
<slot name="removeButton">
|
21
|
+
<button class="placeholder-button" type="button">X</button>
|
22
|
+
</slot>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
</transition-group>
|
27
|
+
<slot name="after"></slot>
|
28
|
+
</div>
|
29
|
+
</template>
|
30
|
+
|
31
|
+
<script>
|
32
|
+
import "./index.css";
|
33
|
+
import {computed} from "vue";
|
34
|
+
import {fixedElement, moveDownByIndex, moveUpByIndex, removeByIndex} from "../../scx-common/index.js";
|
35
|
+
|
36
|
+
export default {
|
37
|
+
name: "scx-group",
|
38
|
+
props: {
|
39
|
+
modelValue: {
|
40
|
+
type: Array,
|
41
|
+
required: true,
|
42
|
+
default: [],
|
43
|
+
},
|
44
|
+
beforeRemove: {
|
45
|
+
type: Function,
|
46
|
+
default: null
|
47
|
+
},
|
48
|
+
beforeMoveUp: {
|
49
|
+
type: Function,
|
50
|
+
default: null
|
51
|
+
},
|
52
|
+
beforeMoveDown: {
|
53
|
+
type: Function,
|
54
|
+
default: null
|
55
|
+
},
|
56
|
+
loop: {
|
57
|
+
type: Boolean,
|
58
|
+
default: true
|
59
|
+
},
|
60
|
+
showRemoveButton: {
|
61
|
+
type: Boolean,
|
62
|
+
default: true
|
63
|
+
},
|
64
|
+
showMoveButton: {
|
65
|
+
type: Boolean,
|
66
|
+
default: true
|
67
|
+
}
|
68
|
+
},
|
69
|
+
setup(props, ctx) {
|
70
|
+
|
71
|
+
if (!props.modelValue) {
|
72
|
+
ctx.emit("update:modelValue", []);
|
73
|
+
}
|
74
|
+
|
75
|
+
const list = computed({
|
76
|
+
get() {
|
77
|
+
return props.modelValue;
|
78
|
+
},
|
79
|
+
set(value) {
|
80
|
+
ctx.emit("update:modelValue", value);
|
81
|
+
}
|
82
|
+
});
|
83
|
+
|
84
|
+
async function groupItemRemove(index) {
|
85
|
+
if (props.beforeRemove) {
|
86
|
+
//如果返回值是 false 则不添加
|
87
|
+
const result = await props.beforeRemove(list.value[index]);
|
88
|
+
if (!result) {
|
89
|
+
return;
|
90
|
+
}
|
91
|
+
}
|
92
|
+
removeByIndex(list.value, index);
|
93
|
+
}
|
94
|
+
|
95
|
+
async function groupItemMoveUp(index) {
|
96
|
+
if (props.beforeMoveUp) {
|
97
|
+
//如果返回值是 false 则不添加
|
98
|
+
const result = await props.beforeMoveUp(index);
|
99
|
+
if (!result) {
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
}
|
103
|
+
moveUpByIndex(list.value, index, props.loop);
|
104
|
+
}
|
105
|
+
|
106
|
+
async function groupItemMoveDown(index) {
|
107
|
+
if (props.beforeMoveDown) {
|
108
|
+
//如果返回值是 false 则不添加
|
109
|
+
const result = await props.beforeMoveDown(index);
|
110
|
+
if (!result) {
|
111
|
+
return;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
moveDownByIndex(list.value, index, props.loop);
|
115
|
+
}
|
116
|
+
|
117
|
+
function showMoveUp(i) {
|
118
|
+
if (!props.showMoveButton) {
|
119
|
+
return false;
|
120
|
+
}
|
121
|
+
const minIndex = 0;
|
122
|
+
//数据量小的时候没必要显示
|
123
|
+
if (list.value.length <= 2 && i === minIndex) {
|
124
|
+
return false;
|
125
|
+
} else {//数据量大的时候 如果没启用循环 第一项不显示
|
126
|
+
return props.loop ? true : i !== minIndex;
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
function showMoveDown(i) {
|
131
|
+
if (!props.showMoveButton) {
|
132
|
+
return false;
|
133
|
+
}
|
134
|
+
const maxIndex = list.value.length - 1;
|
135
|
+
//数据量小的时候没必要显示
|
136
|
+
if (list.value.length <= 2 && i === maxIndex) {
|
137
|
+
return false;
|
138
|
+
} else { //数据量大的时候 如果没启用循环 最后一项不显示
|
139
|
+
return props.loop ? true : i !== maxIndex;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
return {
|
144
|
+
list,
|
145
|
+
groupItemRemove,
|
146
|
+
fixedElement,
|
147
|
+
groupItemMoveUp,
|
148
|
+
groupItemMoveDown,
|
149
|
+
showMoveUp,
|
150
|
+
showMoveDown
|
151
|
+
};
|
152
|
+
}
|
153
|
+
};
|
154
|
+
</script>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<template>
|
2
|
+
<svg class="scx-icon">
|
3
|
+
<use :href="'#scx-icon_' + icon"></use>
|
4
|
+
</svg>
|
5
|
+
</template>
|
6
|
+
|
7
|
+
<script>
|
8
|
+
import "./index.css";
|
9
|
+
|
10
|
+
export default {
|
11
|
+
name: "scx-icon",
|
12
|
+
props: {
|
13
|
+
icon: {
|
14
|
+
type: String,
|
15
|
+
default: "",
|
16
|
+
required: true
|
17
|
+
}
|
18
|
+
}
|
19
|
+
};
|
20
|
+
</script>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
.scx-input {
|
2
|
+
display: flex;
|
3
|
+
justify-content: space-between;
|
4
|
+
align-self: center;
|
5
|
+
align-items: center;
|
6
|
+
/* 32px 是当前这个元素的高度 默认被内部的 input 撑起来的高度*/
|
7
|
+
border-radius: calc(32px / 2);
|
8
|
+
background: var(--scx-input_bg);
|
9
|
+
box-shadow: var(--scx-input_box-shadow);
|
10
|
+
transition: all 100ms cubic-bezier(0.23, 1, 0.32, 1);
|
11
|
+
padding-left: 10px;
|
12
|
+
padding-right: 10px;
|
13
|
+
box-sizing: border-box;
|
14
|
+
}
|
15
|
+
|
16
|
+
.scx-input > input {
|
17
|
+
width: 100%;
|
18
|
+
outline: none;
|
19
|
+
font-size: 15px;
|
20
|
+
height: 30px;
|
21
|
+
border: 0;
|
22
|
+
background-color: transparent;
|
23
|
+
margin-left: 10px;
|
24
|
+
margin-right: 10px;
|
25
|
+
}
|
26
|
+
|
27
|
+
.scx-input > input::placeholder {
|
28
|
+
color: var(--scx-text-placeholder-color);
|
29
|
+
}
|
30
|
+
|
31
|
+
.scx-input > svg {
|
32
|
+
width: 20px;
|
33
|
+
flex-shrink: 0;
|
34
|
+
cursor: pointer;
|
35
|
+
}
|
36
|
+
|
37
|
+
.scx-input:focus-within {
|
38
|
+
box-shadow: var(--scx-input-hover_box-shadow);
|
39
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="scx-input">
|
3
|
+
<scx-icon v-if="icon" :icon="icon"/>
|
4
|
+
<input v-model="m" :placeholder="placeholder" type="text"/>
|
5
|
+
<scx-icon v-show="m!==''" icon="outlined-close" @click="m=''"/>
|
6
|
+
</div>
|
7
|
+
</template>
|
8
|
+
|
9
|
+
<script>
|
10
|
+
import "./index.css";
|
11
|
+
import {computed} from "vue";
|
12
|
+
|
13
|
+
export default {
|
14
|
+
name: "scx-input",
|
15
|
+
props: {
|
16
|
+
modelValue: {
|
17
|
+
type: String,
|
18
|
+
required: true,
|
19
|
+
default: "",
|
20
|
+
},
|
21
|
+
placeholder: {
|
22
|
+
type: String,
|
23
|
+
default: "输入名称搜索",
|
24
|
+
},
|
25
|
+
icon: {
|
26
|
+
type: String,
|
27
|
+
default: null
|
28
|
+
}
|
29
|
+
},
|
30
|
+
setup(props, context) {
|
31
|
+
|
32
|
+
const m = computed({
|
33
|
+
get() {
|
34
|
+
return props.modelValue;
|
35
|
+
},
|
36
|
+
set(value) {
|
37
|
+
context.emit("update:modelValue", value);
|
38
|
+
}
|
39
|
+
});
|
40
|
+
|
41
|
+
return {m};
|
42
|
+
}
|
43
|
+
};
|
44
|
+
</script>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
.scx-panel {
|
2
|
+
border-radius: 4px;
|
3
|
+
padding: 10px;
|
4
|
+
background: var(--scx-glass-bg);
|
5
|
+
transition: background-color 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
|
6
|
+
backdrop-filter: var(--scx-glass-bg-filter);
|
7
|
+
overflow: hidden;
|
8
|
+
display: flex;
|
9
|
+
flex-direction: column;
|
10
|
+
box-shadow: var(--scx-box-shadow-center);
|
11
|
+
}
|
12
|
+
|
13
|
+
/********************* vue 过渡动画 *******************/
|
14
|
+
.scx-panel-t_left-bottom-enter-active,
|
15
|
+
.scx-panel-t_left-bottom-leave-active,
|
16
|
+
.scx-panel-t_left-top-enter-active,
|
17
|
+
.scx-panel-t_left-top-leave-active,
|
18
|
+
.scx-panel-t_top-enter-active,
|
19
|
+
.scx-panel-t_top-leave-active {
|
20
|
+
opacity: 1;
|
21
|
+
transition: transform 200ms cubic-bezier(.23, 1, .32, 1), opacity 100ms cubic-bezier(.23, 1, .32, 1);
|
22
|
+
}
|
23
|
+
|
24
|
+
.scx-panel-t_left-top-enter-from,
|
25
|
+
.scx-panel-t_left-top-leave-to,
|
26
|
+
.scx-panel-t_left-bottom-enter-from,
|
27
|
+
.scx-panel-t_left-bottom-leave-to,
|
28
|
+
.scx-panel-t_top-enter-from,
|
29
|
+
.scx-panel-t_top-leave-to {
|
30
|
+
opacity: 0;
|
31
|
+
transform: scale(0.95);
|
32
|
+
}
|
33
|
+
|
34
|
+
.scx-panel-t_left-bottom-enter-active,
|
35
|
+
.scx-panel-t_left-bottom-leave-active {
|
36
|
+
transform-origin: left bottom;
|
37
|
+
}
|
38
|
+
|
39
|
+
.scx-panel-t_left-top-enter-active,
|
40
|
+
.scx-panel-t_left-top-leave-active {
|
41
|
+
transform-origin: left top;
|
42
|
+
}
|
43
|
+
|
44
|
+
.scx-panel-t_top-enter-active,
|
45
|
+
.scx-panel-t_top-leave-active {
|
46
|
+
transform-origin: top;
|
47
|
+
}
|