111
This commit is contained in:
@@ -1,79 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="my-input">
|
|
||||||
<span class="decorate"></span>
|
|
||||||
<span v-show="icon" class="icon">
|
|
||||||
<svg-icon :name="icon" :size="iconSize" size-unit="px" />
|
|
||||||
</span>
|
|
||||||
<span v-show="before" class="before">{{ before }}</span>
|
|
||||||
<input v-bind="attrs" :value="modelValue" @input="onInput" @copy.stop @keydown.stop />
|
|
||||||
<span v-show="after" class="after">{{ after }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, useAttrs, watch } from 'vue'
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: { type: [String, Number] },
|
|
||||||
icon: { default: '', type: String },
|
|
||||||
iconSize: { default: '10', type: [Number, String] },
|
|
||||||
before: { default: '', type: String },
|
|
||||||
after: { default: '', type: String }
|
|
||||||
})
|
|
||||||
const attrs = useAttrs()
|
|
||||||
const emit = defineEmits(['update:modelValue', 'input'])
|
|
||||||
const onInput = (e) => {
|
|
||||||
var value = e.target.value
|
|
||||||
if (attrs.type === 'number') value = Number(value)
|
|
||||||
emit('update:modelValue', value)
|
|
||||||
emit('input', value)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped lang="less">
|
|
||||||
.my-input {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid rgba(230, 230, 231, 1);
|
|
||||||
border-radius: 1.7px;
|
|
||||||
height: 17px;
|
|
||||||
padding: 0 4px 0 2px;
|
|
||||||
> .decorate {
|
|
||||||
width: 2px;
|
|
||||||
background-color: rgba(230, 230, 231, 1);
|
|
||||||
border-radius: 3px;
|
|
||||||
height: 85%;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
> .iconfont {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #000;
|
|
||||||
margin-right: 2px;
|
|
||||||
}
|
|
||||||
> .before {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #000;
|
|
||||||
margin-right: 2px;
|
|
||||||
}
|
|
||||||
> .after {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #000;
|
|
||||||
margin-left: 1px;
|
|
||||||
}
|
|
||||||
> input {
|
|
||||||
font-size: 12px;
|
|
||||||
width: 0;
|
|
||||||
flex: 1;
|
|
||||||
text-align: right;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 0;
|
|
||||||
-moz-appearance: textfield; /* Firefox */
|
|
||||||
&::-webkit-outer-spin-button,
|
|
||||||
&::-webkit-inner-spin-button {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="my-select">
|
|
||||||
<el-select :model-value="modelValue" @change="onChange" v-bind="attrs">
|
|
||||||
<el-option v-for="v in list" :key="v.value" :label="v.label" :value="v.value" />
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, useAttrs, watch } from 'vue'
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: { required: true },
|
|
||||||
list: { default: () => [], type: [Array, Object] }
|
|
||||||
})
|
|
||||||
const attrs = useAttrs()
|
|
||||||
const emit = defineEmits(['update:modelValue', 'change'])
|
|
||||||
const onChange = (value) => {
|
|
||||||
emit('update:modelValue', value)
|
|
||||||
emit('change', value)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped lang="less">
|
|
||||||
.my-select {
|
|
||||||
&:deep(.el-select) {
|
|
||||||
--el-select-input-font-size: 12px;
|
|
||||||
.el-select__wrapper {
|
|
||||||
font-size: 12px;
|
|
||||||
min-height: 0;
|
|
||||||
height: 28px;
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
.el-select__selected-item,
|
|
||||||
.el-select__input-wrapper,
|
|
||||||
.el-select__placeholder {
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
.el-select__input {
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.el-popper {
|
|
||||||
.el-select-dropdown {
|
|
||||||
li {
|
|
||||||
padding-left: 8px;
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="my-textarea">
|
|
||||||
<textarea
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:value="modelValue"
|
|
||||||
@input="onInput"
|
|
||||||
@change="onChange"
|
|
||||||
@copy.stop
|
|
||||||
@keydown.stop
|
|
||||||
></textarea>
|
|
||||||
<div class="bths">
|
|
||||||
<button><svg-icon name="mobang" size="10" size-unit="px" /></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed, ref, markRaw, onMounted } from 'vue'
|
|
||||||
const emit = defineEmits(['update:modelValue', 'input', 'change'])
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: { type: String },
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: 'Enter the scene you want to describe...'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const onInput = (e) => {
|
|
||||||
const value = e.target.value
|
|
||||||
emit('update:modelValue', value)
|
|
||||||
emit('input', value)
|
|
||||||
}
|
|
||||||
const onChange = (e) => {
|
|
||||||
emit('change', e.target.value)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.my-textarea {
|
|
||||||
width: 100%;
|
|
||||||
height: 115px;
|
|
||||||
border: 1px solid #e4e4e7;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 10px 5px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
> textarea {
|
|
||||||
padding: 0 5px;
|
|
||||||
width: 100%;
|
|
||||||
flex: 1;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
resize: none;
|
|
||||||
font-family: Medium;
|
|
||||||
font-size: 10px;
|
|
||||||
color: #333;
|
|
||||||
&::placeholder {
|
|
||||||
color: #c9c9c9;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 4px;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 4px;
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
border-radius: 4px;
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .bths {
|
|
||||||
padding: 5px 5px 0;
|
|
||||||
> button {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
border: 1px solid #e4e4e7;
|
|
||||||
&:active {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="offset-tool">
|
|
||||||
<div class="input" v-show="showInput">
|
|
||||||
<my-input v-model="left" type="number" before="X" after="%" :min="-100" :max="100" />
|
|
||||||
<my-input v-model="top" type="number" before="Y" after="%" :min="-100" :max="100" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="dish"
|
|
||||||
@mousedown="mousedown"
|
|
||||||
@touchstart="mousedown"
|
|
||||||
ref="dishRef"
|
|
||||||
v-show="showDish"
|
|
||||||
>
|
|
||||||
<img src="/src/assets/images/icon/xyz.png" />
|
|
||||||
<span class="ball" :style="ballStyle"></span>
|
|
||||||
<span class="tip x">X: {{ left }}%</span>
|
|
||||||
<span class="tip y">Y: {{ top }}%</span>
|
|
||||||
<span class="line x"></span>
|
|
||||||
<span class="line y"></span>
|
|
||||||
<span class="line z" :style="lineZStyle"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, watch, computed } from 'vue'
|
|
||||||
import MyInput from './my-input.vue'
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: { type: Object as () => { x: number; y: number } },
|
|
||||||
showInput: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
showDish: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const emit = defineEmits(['update:modelValue', 'change', 'input'])
|
|
||||||
// 工具的实际坐标 -100 ~ 100
|
|
||||||
const top = ref(Math.round(props.modelValue.y))
|
|
||||||
const left = ref(Math.round(props.modelValue.x))
|
|
||||||
|
|
||||||
// 原点的坐标 0 ~ 100
|
|
||||||
const ballStyle = computed(() => ({
|
|
||||||
top: 50 + top.value / 2 + '%',
|
|
||||||
left: 50 + left.value / 2 + '%'
|
|
||||||
}))
|
|
||||||
watch(
|
|
||||||
() => props.modelValue.x,
|
|
||||||
(v) => (left.value = v)
|
|
||||||
)
|
|
||||||
watch(
|
|
||||||
() => props.modelValue.y,
|
|
||||||
(v) => (top.value = v)
|
|
||||||
)
|
|
||||||
const dishRef = ref<HTMLDivElement>()
|
|
||||||
const mousedown = (e: MouseEvent | TouchEvent) => {
|
|
||||||
if (!dishRef.value) return
|
|
||||||
const mousemove = (e: MouseEvent | TouchEvent) => {
|
|
||||||
if (!dishRef.value) return
|
|
||||||
const rect = dishRef.value.getBoundingClientRect()
|
|
||||||
const X = e.clientX || (e as TouchEvent).touches[0].clientX
|
|
||||||
const Y = e.clientY || (e as TouchEvent).touches[0].clientY
|
|
||||||
var x = ((X - rect.left) / rect.width) * 100
|
|
||||||
var y = ((Y - rect.top) / rect.height) * 100
|
|
||||||
if (x < 0) x = 0
|
|
||||||
if (x > 100) x = 100
|
|
||||||
if (y < 0) y = 0
|
|
||||||
if (y > 100) y = 100
|
|
||||||
left.value = Math.round((x - 50) * 2)
|
|
||||||
top.value = Math.round((y - 50) * 2)
|
|
||||||
onInput()
|
|
||||||
}
|
|
||||||
mousemove(e)
|
|
||||||
const mouseup = () => {
|
|
||||||
onChange()
|
|
||||||
document.removeEventListener('mousemove', mousemove)
|
|
||||||
document.removeEventListener('touchmove', mousemove)
|
|
||||||
document.removeEventListener('mouseup', mouseup)
|
|
||||||
document.removeEventListener('touchend', mouseup)
|
|
||||||
}
|
|
||||||
document.addEventListener('mousemove', mousemove)
|
|
||||||
document.addEventListener('touchmove', mousemove)
|
|
||||||
document.addEventListener('mouseup', mouseup)
|
|
||||||
document.addEventListener('touchend', mouseup)
|
|
||||||
}
|
|
||||||
const onInput = () => {
|
|
||||||
const value = {
|
|
||||||
x: left.value,
|
|
||||||
y: top.value
|
|
||||||
}
|
|
||||||
emit('update:modelValue', value)
|
|
||||||
emit('input', value)
|
|
||||||
}
|
|
||||||
var changeTime: any = null
|
|
||||||
const onChange = () => {
|
|
||||||
clearTimeout(changeTime)
|
|
||||||
changeTime = setTimeout(() => {
|
|
||||||
const value = {
|
|
||||||
x: left.value,
|
|
||||||
y: top.value
|
|
||||||
}
|
|
||||||
emit('update:modelValue', value)
|
|
||||||
emit('change', value)
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
const lineZStyle = computed(() => ({
|
|
||||||
'--rotateZ': calculateAngle(0, 0, left.value, top.value) + 'deg',
|
|
||||||
width: calculateDistance(0, 0, left.value, top.value) / 2 + '%'
|
|
||||||
}))
|
|
||||||
// 计算角度
|
|
||||||
function calculateAngle(x1: number, y1: number, x2: number, y2: number) {
|
|
||||||
const deltaX = x2 - x1
|
|
||||||
const deltaY = y1 - y2
|
|
||||||
let angle = Math.atan2(deltaX, deltaY) * (180 / Math.PI) - 90
|
|
||||||
return angle
|
|
||||||
}
|
|
||||||
// 计算距离
|
|
||||||
function calculateDistance(x1: number, y1: number, x2: number, y2: number) {
|
|
||||||
const deltaX = x2 - x1
|
|
||||||
const deltaY = y2 - y1
|
|
||||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
|
|
||||||
return distance
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.offset-tool {
|
|
||||||
position: relative;
|
|
||||||
> .input {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
> * {
|
|
||||||
flex: 1;
|
|
||||||
margin-right: 10px;
|
|
||||||
&:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .dish {
|
|
||||||
width: 115px;
|
|
||||||
height: 115px;
|
|
||||||
border: 1px solid #eaeaea;
|
|
||||||
border-radius: 3.4px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
margin-top: 20px;
|
|
||||||
> * {
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
> img {
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
bottom: 3.5px;
|
|
||||||
right: 3.5px;
|
|
||||||
}
|
|
||||||
> .ball {
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 8.5px;
|
|
||||||
height: 8.5px;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
background-color: #333;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0px 0.68px 0.17px 0px rgba(0, 0, 0, 0.26);
|
|
||||||
}
|
|
||||||
> .tip {
|
|
||||||
font-size: 8.5px;
|
|
||||||
color: #000;
|
|
||||||
line-height: 24px;
|
|
||||||
&.x {
|
|
||||||
top: 50%;
|
|
||||||
right: 0%;
|
|
||||||
transform: translate(100%, -50%);
|
|
||||||
padding-left: 6px;
|
|
||||||
}
|
|
||||||
&.y {
|
|
||||||
top: 0%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .line {
|
|
||||||
border-color: #d9d9d9;
|
|
||||||
border-style: dashed;
|
|
||||||
border-width: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
&.x {
|
|
||||||
width: 100%;
|
|
||||||
border-top-width: 1px;
|
|
||||||
}
|
|
||||||
&.y {
|
|
||||||
height: 100%;
|
|
||||||
border-left-width: 1px;
|
|
||||||
}
|
|
||||||
&.z {
|
|
||||||
width: 50%;
|
|
||||||
border-top-width: 1px;
|
|
||||||
border-color: #454754;
|
|
||||||
transform: translate(0%, -50%) rotateZ(var(--rotateZ));
|
|
||||||
transform-origin: left center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="pixel-ratio-selection">
|
|
||||||
<div
|
|
||||||
v-for="v in list"
|
|
||||||
:key="v"
|
|
||||||
:class="{ active: v === modelValue }"
|
|
||||||
@click="onChange(v)"
|
|
||||||
:style="{ '--w': v.split(':')[0], '--h': v.split(':')[1] }"
|
|
||||||
>
|
|
||||||
{{ v }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { reactive, defineExpose } from 'vue'
|
|
||||||
const emit = defineEmits(['update:modelValue', 'change'])
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: { type: String },
|
|
||||||
list: {
|
|
||||||
type: Array,
|
|
||||||
default: () => ['1:1', '4:3', '3:4', '16:9']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const data = reactive({})
|
|
||||||
const onChange = (v) => {
|
|
||||||
emit('update:modelValue', v)
|
|
||||||
emit('change', v)
|
|
||||||
}
|
|
||||||
defineExpose({ data })
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.pixel-ratio-selection {
|
|
||||||
width: 100%;
|
|
||||||
height: 34px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 0 17px;
|
|
||||||
user-select: none;
|
|
||||||
> div {
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #7c7c7c;
|
|
||||||
height: 21px;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
&.active {
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
border-radius: 1px;
|
|
||||||
border: 1px solid #7c7c7c;
|
|
||||||
width: calc(var(--w) / max(var(--w), var(--h)) * 10px);
|
|
||||||
height: calc(var(--h) / max(var(--w), var(--h)) * 10px);
|
|
||||||
margin-right: 4px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="slider" :disabled="disabled">
|
|
||||||
<div
|
|
||||||
class="input-range"
|
|
||||||
:style="{
|
|
||||||
'--progress': (value - props.min) / (props.max - props.min)
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span class="tip">{{ props.tipFormatter(value) }}</span>
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
v-model="value"
|
|
||||||
v-bind="$attrs"
|
|
||||||
@input="onInput"
|
|
||||||
@change="onChange"
|
|
||||||
:disabled="disabled"
|
|
||||||
:min="props.min"
|
|
||||||
:max="props.max"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="input" v-show="isInput">
|
|
||||||
<my-input
|
|
||||||
type="number"
|
|
||||||
v-model="value"
|
|
||||||
v-bind="$attrs"
|
|
||||||
@input="onInput"
|
|
||||||
@change="onChange"
|
|
||||||
:disabled="disabled"
|
|
||||||
:min="props.min"
|
|
||||||
:max="props.max"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, defineProps, defineEmits, watch } from 'vue'
|
|
||||||
import MyInput from './my-input.vue'
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: { type: Number },
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
min: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
max: {
|
|
||||||
type: Number,
|
|
||||||
default: 100
|
|
||||||
},
|
|
||||||
tipFormatter: {
|
|
||||||
type: Function,
|
|
||||||
default: (v) => v
|
|
||||||
},
|
|
||||||
isInput: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const emit = defineEmits(['update:modelValue', 'change', 'input'])
|
|
||||||
watch(
|
|
||||||
() => props.modelValue,
|
|
||||||
(v) => {
|
|
||||||
value.value = v
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const value = ref(props.modelValue)
|
|
||||||
const onInput = () => {
|
|
||||||
if (props.disabled) return
|
|
||||||
const v = Number(value.value)
|
|
||||||
emit('update:modelValue', v)
|
|
||||||
emit('input', v)
|
|
||||||
}
|
|
||||||
const onChange = () => {
|
|
||||||
if (props.disabled) return
|
|
||||||
const v = Number(value.value)
|
|
||||||
emit('update:modelValue', v)
|
|
||||||
emit('change', v)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.slider {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
--input-thumb-size: 8px;
|
|
||||||
--backcolor1: var(--slider-thumb-color1, #4285f4);
|
|
||||||
--backcolor2: var(--slider-thumb-color2, rgba(0, 0, 0, 0.1));
|
|
||||||
&:hover {
|
|
||||||
> .input-range > .tip {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .input-range {
|
|
||||||
position: relative;
|
|
||||||
flex: 2;
|
|
||||||
display: flex;
|
|
||||||
> input {
|
|
||||||
width: 100%;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
height: 3px;
|
|
||||||
border-radius: 3px;
|
|
||||||
outline: none;
|
|
||||||
background: linear-gradient(
|
|
||||||
to right,
|
|
||||||
var(--backcolor1) 0%,
|
|
||||||
var(--backcolor1) calc(var(--progress) * 100%),
|
|
||||||
var(--backcolor2) calc(var(--progress) * 100%),
|
|
||||||
var(--backcolor2) 100%
|
|
||||||
);
|
|
||||||
&::-webkit-slider-thumb {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
width: var(--input-thumb-size);
|
|
||||||
height: var(--input-thumb-size);
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--backcolor1); /* 蓝色滑块 */
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
&::-webkit-slider-thumb:hover {
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .tip {
|
|
||||||
position: absolute;
|
|
||||||
font-size: 10px;
|
|
||||||
pointer-events: none;
|
|
||||||
user-select: none;
|
|
||||||
color: #666;
|
|
||||||
top: calc(var(--input-thumb-size) / -2 - 3.5px);
|
|
||||||
left: calc(
|
|
||||||
(100% - var(--input-thumb-size)) * var(--progress) + var(--input-thumb-size) / 2
|
|
||||||
);
|
|
||||||
transform: translate(-50%, -100%);
|
|
||||||
background-color: rgba(0, 0, 0, 0.7);
|
|
||||||
color: white;
|
|
||||||
padding: 3px 5px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
pointer-events: none;
|
|
||||||
display: none;
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 97%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-left: 5px solid transparent;
|
|
||||||
border-right: 5px solid transparent;
|
|
||||||
border-top: 5px solid rgba(0, 0, 0, 0.8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .input {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: 10px;
|
|
||||||
> input {
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="upload-file">
|
|
||||||
<div class="preview" v-if="url">
|
|
||||||
<img :src="url" @error="onChange(null)" />
|
|
||||||
<div class="close" @click="onChange(null)">
|
|
||||||
<svg-icon name="close-border" size="16" size-unit="px" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="control" v-else>
|
|
||||||
<div class="icon"><svg-icon name="upload" size="17" size-unit="px" /></div>
|
|
||||||
<div class="txt">{{ tip }}</div>
|
|
||||||
<div class="btn" @click="onSelectFile">Select File</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { reactive, computed } from 'vue'
|
|
||||||
const emit = defineEmits(['update:modelValue', 'change'])
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: { type: [File, Object, String, null] },
|
|
||||||
tip: { type: String, default: 'Upload your files' }
|
|
||||||
})
|
|
||||||
const data = reactive({
|
|
||||||
file: null
|
|
||||||
})
|
|
||||||
const url = computed(() => {
|
|
||||||
const type = props.modelValue?.constructor
|
|
||||||
var str = ''
|
|
||||||
if (type === File) {
|
|
||||||
str = URL.createObjectURL(props.modelValue as File)
|
|
||||||
} else if (type === String) {
|
|
||||||
str = props.modelValue as string
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
})
|
|
||||||
const onChange = (v) => {
|
|
||||||
emit('update:modelValue', v)
|
|
||||||
emit('change', v)
|
|
||||||
}
|
|
||||||
const onSelectFile = () => {
|
|
||||||
const input = document.createElement('input')
|
|
||||||
input.type = 'file'
|
|
||||||
input.accept = 'image/png, image/jpeg, image/jpg'
|
|
||||||
input.addEventListener('change', (e) => {
|
|
||||||
const file = e.target.files[0]
|
|
||||||
if (file) onChange(file)
|
|
||||||
})
|
|
||||||
input.click()
|
|
||||||
}
|
|
||||||
defineExpose({ data })
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.upload-file {
|
|
||||||
width: 100%;
|
|
||||||
height: 99px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
> .control {
|
|
||||||
text-align: center;
|
|
||||||
> .txt {
|
|
||||||
margin-top: 6px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: 8px;
|
|
||||||
color: #7c7c7c;
|
|
||||||
}
|
|
||||||
> .btn {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-shadow: 0px 0.75px 0px 0px rgba(0, 0, 0, 0.02);
|
|
||||||
min-width: 39px;
|
|
||||||
height: 13px;
|
|
||||||
border-radius: 2.3px;
|
|
||||||
background-color: #fff;
|
|
||||||
font-size: 6px;
|
|
||||||
color: #000;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
cursor: pointer;
|
|
||||||
&:active {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .preview {
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
position: relative;
|
|
||||||
> img {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
> .close {
|
|
||||||
position: absolute;
|
|
||||||
top: 0.1px;
|
|
||||||
right: 0.1px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
Reference in New Issue
Block a user