|
|
|
|
@ -0,0 +1,530 @@
|
|
|
|
|
<template>
|
|
|
|
|
<view v-if="visible" class="overlay" @touchmove.prevent.self>
|
|
|
|
|
<view class="box" :style="boxStyle" @touchstart.stop.prevent="onDragStartTouch"
|
|
|
|
|
@mousedown.stop.prevent="onDragStartMouse">
|
|
|
|
|
<view class="header" ref="headerRef">
|
|
|
|
|
<view class="title">{{ title }}</view>
|
|
|
|
|
<view class="actions">
|
|
|
|
|
<button class="btn" @click="cancel">取消</button>
|
|
|
|
|
<button class="btn" @click="confirm">确定</button>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<picker-view class="picker-view" :style="{
|
|
|
|
|
height: pickerHeight + 'px',
|
|
|
|
|
'--item-h': ITEM_H + 'px',
|
|
|
|
|
'--cols': displayColumns.length || 1
|
|
|
|
|
}" :value="normalizedSelectedIndexes" @change="onPickerChange">
|
|
|
|
|
<picker-view-column class="picker-view-column" v-for="(col, ci) in displayColumns" :key="ci">
|
|
|
|
|
<view v-for="(item, i) in col" :key="i" class="picker-item">{{ item }}</view>
|
|
|
|
|
<!-- :style="{ height: ITEM_H + 'px', lineHeight: ITEM_H + 'px' }" -->
|
|
|
|
|
</picker-view-column>
|
|
|
|
|
</picker-view>
|
|
|
|
|
|
|
|
|
|
<view class="resize-handle" @touchstart.stop.prevent="onResizeStartTouch"
|
|
|
|
|
@mousedown.stop.prevent="onResizeStartMouse">
|
|
|
|
|
<view class="grip"></view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import {
|
|
|
|
|
ref,
|
|
|
|
|
computed,
|
|
|
|
|
watch,
|
|
|
|
|
onMounted,
|
|
|
|
|
onBeforeUnmount,
|
|
|
|
|
nextTick
|
|
|
|
|
} from 'vue';
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
modelValue: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false
|
|
|
|
|
},
|
|
|
|
|
title: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: '请选择'
|
|
|
|
|
},
|
|
|
|
|
initLeft: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 50
|
|
|
|
|
},
|
|
|
|
|
initTop: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 100
|
|
|
|
|
},
|
|
|
|
|
initWidth: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 320
|
|
|
|
|
},
|
|
|
|
|
initHeight: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 320
|
|
|
|
|
},
|
|
|
|
|
minWidth: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 180
|
|
|
|
|
},
|
|
|
|
|
minHeight: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 200
|
|
|
|
|
},
|
|
|
|
|
maxWidth: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 1000
|
|
|
|
|
},
|
|
|
|
|
maxHeight: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 1200
|
|
|
|
|
},
|
|
|
|
|
columns: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default: () => [
|
|
|
|
|
[]
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
nameKey: {
|
|
|
|
|
type: [String, Array],
|
|
|
|
|
default: 'name'
|
|
|
|
|
},
|
|
|
|
|
value: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default: () => []
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
const emit = defineEmits(['update:modelValue', 'confirm', 'change', 'update:position', 'update:size']);
|
|
|
|
|
|
|
|
|
|
/* ========== 基本可见性 / 同步 ========== */
|
|
|
|
|
const visible = ref(props.modelValue);
|
|
|
|
|
watch(() => props.modelValue, v => visible.value = v);
|
|
|
|
|
watch(visible, v => emit('update:modelValue', v));
|
|
|
|
|
|
|
|
|
|
/* ========== 位置 / 尺寸 ========== */
|
|
|
|
|
const left = ref(props.initLeft);
|
|
|
|
|
const top = ref(props.initTop);
|
|
|
|
|
const width = ref(props.initWidth);
|
|
|
|
|
const height = ref(props.initHeight);
|
|
|
|
|
|
|
|
|
|
/* ========== 固定行高(关键:整数像素) ========== */
|
|
|
|
|
const ITEM_H = 44; // 行高(px),保持整数像素
|
|
|
|
|
const HEADER_H = 44; // header 高度(px),与你样式一致
|
|
|
|
|
|
|
|
|
|
/* ========== selectedIndexes 初始值 & 正规化 ========== */
|
|
|
|
|
const selectedIndexes = ref(
|
|
|
|
|
(props.value && props.value.length) ? props.value.map(v => Number(v || 0)) : (Array.isArray(props.columns) ?
|
|
|
|
|
props.columns.map(() => 0) : [])
|
|
|
|
|
);
|
|
|
|
|
watch(() => props.value, v => {
|
|
|
|
|
if (v && v.length) {
|
|
|
|
|
selectedIndexes.value = v.map(x => Number(x || 0));
|
|
|
|
|
clampSelectedIndexes();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
const normalizedSelectedIndexes = computed(() => selectedIndexes.value.map(v => Number(v || 0)));
|
|
|
|
|
|
|
|
|
|
/* ========== columns -> displayColumns(显示文本) ========== */
|
|
|
|
|
function getByPath(obj, path) {
|
|
|
|
|
if (obj == null) return undefined;
|
|
|
|
|
if (!path) return obj;
|
|
|
|
|
const parts = path.split('.');
|
|
|
|
|
let cur = obj;
|
|
|
|
|
for (let p of parts) {
|
|
|
|
|
if (cur == null) return undefined;
|
|
|
|
|
cur = cur[p];
|
|
|
|
|
}
|
|
|
|
|
return cur;
|
|
|
|
|
}
|
|
|
|
|
const displayColumns = computed(() => {
|
|
|
|
|
const raw = props.columns || [];
|
|
|
|
|
const nk = props.nameKey;
|
|
|
|
|
return raw.map((col, ci) => {
|
|
|
|
|
if (!Array.isArray(col)) return [];
|
|
|
|
|
return col.map(item => {
|
|
|
|
|
if (item == null) return '';
|
|
|
|
|
if (typeof item === 'object') {
|
|
|
|
|
let keyToUse = Array.isArray(nk) ? (nk[ci] !== undefined ? nk[ci] : nk[0]) :
|
|
|
|
|
nk;
|
|
|
|
|
if (!keyToUse) {
|
|
|
|
|
return item.name ?? item.label ?? item.title ?? String(item);
|
|
|
|
|
}
|
|
|
|
|
const val = getByPath(item, keyToUse);
|
|
|
|
|
return (val === undefined || val === null) ? (item.name ?? item.label ?? item
|
|
|
|
|
.title ?? String(item)) : String(val);
|
|
|
|
|
} else {
|
|
|
|
|
return String(item);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
const rawColumns = computed(() => (props.columns || []).map(col => Array.isArray(col) ? col : []));
|
|
|
|
|
|
|
|
|
|
/* ========== 屏幕信息(只用 uni.getSystemInfoSync,App/H5/小程序 均可用) ========== */
|
|
|
|
|
let screenW = 800,
|
|
|
|
|
screenH = 600; // 安全默认值(不会访问 window)
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
try {
|
|
|
|
|
const info = uni.getSystemInfoSync();
|
|
|
|
|
// uni.getSystemInfoSync 在 App/H5/小程序 都可用,优先使用
|
|
|
|
|
screenW = info.windowWidth || info.screenWidth || screenW;
|
|
|
|
|
screenH = info.windowHeight || info.screenHeight || screenH;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// 若异常,不尝试访问 window,保留默认值
|
|
|
|
|
screenW = screenW;
|
|
|
|
|
screenH = screenH;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* ========== 拖拽 / 缩放(保持原逻辑) ========== */
|
|
|
|
|
let dragging = false;
|
|
|
|
|
let dragStart = {
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
left: 0,
|
|
|
|
|
top: 0
|
|
|
|
|
};
|
|
|
|
|
let resizing = false;
|
|
|
|
|
let resizeStart = {
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
w: 0,
|
|
|
|
|
h: 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function onDragStartTouch(e) {
|
|
|
|
|
const t = e.touches && e.touches[0];
|
|
|
|
|
if (t) startDrag(t.clientX, t.clientY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onDragStartMouse(e) {
|
|
|
|
|
startDrag(e.clientX, e.clientY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function startDrag(cx, cy) {
|
|
|
|
|
dragging = true;
|
|
|
|
|
dragStart.x = cx;
|
|
|
|
|
dragStart.y = cy;
|
|
|
|
|
dragStart.left = left.value;
|
|
|
|
|
dragStart.top = top.value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onResizeStartTouch(e) {
|
|
|
|
|
const t = e.touches && e.touches[0];
|
|
|
|
|
if (t) startResize(t.clientX, t.clientY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onResizeStartMouse(e) {
|
|
|
|
|
startResize(e.clientX, e.clientY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function startResize(cx, cy) {
|
|
|
|
|
resizing = true;
|
|
|
|
|
resizeStart.x = cx;
|
|
|
|
|
resizeStart.y = cy;
|
|
|
|
|
resizeStart.w = width.value;
|
|
|
|
|
resizeStart.h = height.value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onTouchMove(e) {
|
|
|
|
|
if (!dragging && !resizing) return;
|
|
|
|
|
const t = e.touches && e.touches[0];
|
|
|
|
|
if (t) handleMove(t.clientX, t.clientY, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onMouseMove(e) {
|
|
|
|
|
if (!dragging && !resizing) return;
|
|
|
|
|
handleMove(e.clientX, e.clientY, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleMove(cx, cy, e) {
|
|
|
|
|
if (dragging) {
|
|
|
|
|
const dx = cx - dragStart.x;
|
|
|
|
|
const dy = cy - dragStart.y;
|
|
|
|
|
left.value = Math.min(Math.max(0, dragStart.left + dx), Math.max(0, screenW - width.value));
|
|
|
|
|
top.value = Math.min(Math.max(0, dragStart.top + dy), Math.max(0, screenH - height.value));
|
|
|
|
|
emit('update:position', {
|
|
|
|
|
left: left.value,
|
|
|
|
|
top: top.value
|
|
|
|
|
});
|
|
|
|
|
} else if (resizing) {
|
|
|
|
|
const dx = cx - resizeStart.x;
|
|
|
|
|
const dy = cy - resizeStart.y;
|
|
|
|
|
let nw = Math.min(props.maxWidth, Math.max(props.minWidth, Math.round(resizeStart.w + dx)));
|
|
|
|
|
let nh = Math.min(props.maxHeight, Math.max(props.minHeight, Math.round(resizeStart.h + dy)));
|
|
|
|
|
if (left.value + nw > screenW) nw = screenW - left.value;
|
|
|
|
|
if (top.value + nh > screenH) nh = screenH - top.value;
|
|
|
|
|
width.value = nw;
|
|
|
|
|
height.value = nh;
|
|
|
|
|
emit('update:size', {
|
|
|
|
|
width: width.value,
|
|
|
|
|
height: height.value
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (e && e.preventDefault) e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onMouseUp() {
|
|
|
|
|
if (dragging) dragging = false;
|
|
|
|
|
if (resizing) resizing = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onTouchEnd() {
|
|
|
|
|
if (dragging) dragging = false;
|
|
|
|
|
if (resizing) resizing = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ========== picker 行数 / 高度 计算(关键) ========== */
|
|
|
|
|
const pickerHeight = computed(() => {
|
|
|
|
|
const avail = Math.max(0, Math.round(height.value) - HEADER_H);
|
|
|
|
|
let rows = Math.floor(avail / ITEM_H);
|
|
|
|
|
if (rows < 1) rows = 1;
|
|
|
|
|
if (rows % 2 === 0) rows = rows - 1 > 0 ? rows - 1 : 1;
|
|
|
|
|
return rows * ITEM_H;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* ========== clamp helper ========== */
|
|
|
|
|
function clampSelectedIndexes() {
|
|
|
|
|
const colsArr = rawColumns.value || [];
|
|
|
|
|
if (selectedIndexes.value.length !== colsArr.length) {
|
|
|
|
|
selectedIndexes.value = Array.from({
|
|
|
|
|
length: colsArr.length
|
|
|
|
|
}, (_, i) => selectedIndexes.value[i] ?? 0);
|
|
|
|
|
}
|
|
|
|
|
selectedIndexes.value = selectedIndexes.value.map((idx, ci) => {
|
|
|
|
|
const col = Array.isArray(colsArr[ci]) ? colsArr[ci] : [];
|
|
|
|
|
const maxIdx = Math.max(0, col.length - 1);
|
|
|
|
|
return col.length ? Math.min(Math.max(0, Number(idx) || 0), maxIdx) : 0;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ========== 对齐重置逻辑(核心) ========== */
|
|
|
|
|
let resetTimer = null;
|
|
|
|
|
|
|
|
|
|
function resetPickerAlign(delay = 40) {
|
|
|
|
|
if (resetTimer) clearTimeout(resetTimer);
|
|
|
|
|
resetTimer = setTimeout(() => {
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
clampSelectedIndexes();
|
|
|
|
|
selectedIndexes.value = selectedIndexes.value.map(v => Number(v || 0));
|
|
|
|
|
});
|
|
|
|
|
resetTimer = null;
|
|
|
|
|
}, delay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 在关键变化上调用重置:columns / visible / pickerHeight */
|
|
|
|
|
watch(() => props.columns, () => {
|
|
|
|
|
clampSelectedIndexes();
|
|
|
|
|
resetPickerAlign(30);
|
|
|
|
|
}, {
|
|
|
|
|
deep: true,
|
|
|
|
|
immediate: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
watch(() => visible.value, (v) => {
|
|
|
|
|
if (v) {
|
|
|
|
|
resetPickerAlign(60);
|
|
|
|
|
setTimeout(() => resetPickerAlign(40), 120);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
watch(() => pickerHeight.value, () => {
|
|
|
|
|
resetPickerAlign(30);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* ========== picker change / confirm / cancel ========== */
|
|
|
|
|
function onPickerChange(e) {
|
|
|
|
|
const val = (e && e.detail && e.detail.value) ? e.detail.value : e;
|
|
|
|
|
if (Array.isArray(val)) {
|
|
|
|
|
selectedIndexes.value = val.map((v, ci) => {
|
|
|
|
|
const col = rawColumns.value[ci] || [];
|
|
|
|
|
const max = Math.max(0, col.length - 1);
|
|
|
|
|
const num = Number(v) || 0;
|
|
|
|
|
return col.length ? Math.min(Math.max(0, num), max) : 0;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
emit('change', selectedIndexes.value.slice());
|
|
|
|
|
resetPickerAlign(80);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function confirm() {
|
|
|
|
|
const result = selectedIndexes.value.map((idx, ci) => {
|
|
|
|
|
const col = rawColumns.value[ci] || [];
|
|
|
|
|
const display = (displayColumns.value[ci] && displayColumns.value[ci][idx] !== undefined) ?
|
|
|
|
|
displayColumns.value[ci][idx] : (col[idx] !== undefined ? String(col[idx]) : '');
|
|
|
|
|
const val = col[idx] !== undefined ? col[idx] : (display || null);
|
|
|
|
|
return {
|
|
|
|
|
index: idx,
|
|
|
|
|
value: val,
|
|
|
|
|
display
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
emit('confirm', result);
|
|
|
|
|
visible.value = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cancel() {
|
|
|
|
|
visible.value = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ========== boxStyle ========== */
|
|
|
|
|
const boxStyle = computed(() => ({
|
|
|
|
|
position: 'fixed',
|
|
|
|
|
left: `${Math.round(left.value)}px`,
|
|
|
|
|
top: `${Math.round(top.value)}px`,
|
|
|
|
|
width: `${Math.round(width.value)}px`,
|
|
|
|
|
height: `${Math.round(height.value)}px`,
|
|
|
|
|
zIndex: 1000,
|
|
|
|
|
background: '#fff',
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
boxShadow: '0 8px 24px rgba(0,0,0,0.12)',
|
|
|
|
|
overflow: 'hidden',
|
|
|
|
|
transform: 'translateZ(0)'
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
/* ========== 全局事件监听(更稳健的平台检测) ========== */
|
|
|
|
|
/* 我们优先使用 window/document(H5 环境),如果都不可用(如原生 App),则不添加全局监听,
|
|
|
|
|
因为原生 App 的触摸事件通常在组件内部就能处理(touchstart/touchmove/touchend) */
|
|
|
|
|
let globalEventTarget = null;
|
|
|
|
|
if (typeof window !== 'undefined' && window && typeof window.addEventListener === 'function') {
|
|
|
|
|
globalEventTarget = window;
|
|
|
|
|
} else if (typeof document !== 'undefined' && document && typeof document.addEventListener === 'function') {
|
|
|
|
|
globalEventTarget = document;
|
|
|
|
|
} else {
|
|
|
|
|
globalEventTarget = null; // 在原生 App(非 H5)中通常为 null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
if (globalEventTarget) {
|
|
|
|
|
try {
|
|
|
|
|
globalEventTarget.addEventListener('mousemove', onMouseMove);
|
|
|
|
|
globalEventTarget.addEventListener('mouseup', onMouseUp);
|
|
|
|
|
// touchmove 需要 passive:false 以阻止默认行为
|
|
|
|
|
globalEventTarget.addEventListener('touchmove', onTouchMove, {
|
|
|
|
|
passive: false
|
|
|
|
|
});
|
|
|
|
|
globalEventTarget.addEventListener('touchend', onTouchEnd);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// 如果某些环境不支持 options 参数,用 fallback
|
|
|
|
|
try {
|
|
|
|
|
globalEventTarget.addEventListener('touchmove', onTouchMove);
|
|
|
|
|
globalEventTarget.addEventListener('touchend', onTouchEnd);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
// 忽略
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
if (globalEventTarget) {
|
|
|
|
|
try {
|
|
|
|
|
globalEventTarget.removeEventListener('mousemove', onMouseMove);
|
|
|
|
|
globalEventTarget.removeEventListener('mouseup', onMouseUp);
|
|
|
|
|
globalEventTarget.removeEventListener('touchmove', onTouchMove);
|
|
|
|
|
globalEventTarget.removeEventListener('touchend', onTouchEnd);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// 忽略
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (resetTimer) {
|
|
|
|
|
clearTimeout(resetTimer);
|
|
|
|
|
resetTimer = null;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.overlay {
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
|
|
|
|
z-index: 900;
|
|
|
|
|
background: rgba(0, 0, 0, 0.35);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.box {
|
|
|
|
|
background: #fff;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
user-select: none;
|
|
|
|
|
-webkit-user-select: none;
|
|
|
|
|
transform: translateZ(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header {
|
|
|
|
|
height: 44px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 0 8px;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
background: linear-gradient(90deg, #fff, #fafafa);
|
|
|
|
|
cursor: move;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn {
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-view {
|
|
|
|
|
width: 100%;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
-webkit-overflow-scrolling: touch;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-view-column {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
width: calc(100% / var(--cols, 1));
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.picker-item {
|
|
|
|
|
display: block;
|
|
|
|
|
/* height: var(--item-h);
|
|
|
|
|
line-height: var(--item-h); */
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
user-select: none;
|
|
|
|
|
-webkit-user-select: none;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resize-handle {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 6px;
|
|
|
|
|
bottom: 6px;
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
touch-action: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.grip {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
border: 1px dashed #bbb;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
</style>
|