Compare commits

...

2 Commits

Author SHA1 Message Date
Teng f55de27e87 Merge branch 'main' of http://47.115.223.229:8888/yangjun/hldy_app_mini 2025-11-18 13:08:38 +08:00
Teng 7031bdcdb3 合并代码 2025-11-18 13:08:28 +08:00
4 changed files with 543 additions and 7 deletions

View File

@ -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.getSystemInfoSyncApp/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/documentH5 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>

View File

@ -1,7 +1,9 @@
<template>
<view class="contain">
<view v-show="moreindex!=-1 || topbuttontarget!=-1 || openjianhuo || opengaijia || opendata || opencgr " class="mengban"
@click="moreindex=-1; topbuttontarget=-1;openjianhuo=false;opengaijia=false;opendata=false;opencgr=false"></view>
<view v-show="moreindex!=-1 || topbuttontarget!=-1 || openjianhuo || opengaijia || opendata || opencgr "
class="mengban"
@click="moreindex=-1; topbuttontarget=-1;openjianhuo=false;opengaijia=false;opendata=false;opencgr=false">
</view>
<!-- 日期 -->
<view class="calendar-father" v-show="opendata">
<calendar @datachange="dateget" />
@ -1008,8 +1010,10 @@
</view>
</view>
</view>
<u-select v-model="opencgr" :list="cgrlist" label-name="cgBy" value-name="cgBy" ></u-select>
<u-select v-model="opengys" :list="gyslist" label-name="suppliersName" value-name="suppliers" ></u-select>
<!-- <u-select v-model="opencgr" :list="cgrlist" label-name="cgBy" value-name="cgBy" ></u-select> -->
<!-- <u-select v-model="opengys" :list="gyslist" label-name="suppliersName" value-name="suppliers" ></u-select> -->
<superpicker v-model:modelValue="opengys" :columns="[gyslist]" nameKey="suppliersName" :init-left="80" :init-top="120" :init-width="360"
:init-height="360" />
</view>
</template>
@ -1018,6 +1022,7 @@
import { queryInvoicingList, getCgdMaterialTreeData, queryNuInfoByNuId, updateKfstatus, queryCgdList, queryCgdInfoList, queryWlInfoByWlId, voidedCgdMain, getCgrLis, getGysList } from './api/lunpan.js'
import { onShow, onLoad, onHide, onPageScroll } from "@dcloudio/uni-app"
import calendar from '@/component/public/calendar.vue'
import superpicker from '@/component/public/superpicker.vue'
const typechange = ref(0);
const serverUrl = ref("");
@ -1047,7 +1052,7 @@
})
const opencgr = ref(false);
const opengys = ref(false);
const cgrlist = ref([]);
const gyslist = ref([]);
@ -1057,6 +1062,7 @@
})
getGysList().then((res : any) => {
gyslist.value = res.result;
console.log("isright",res)
})
}

View File

@ -574,7 +574,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
)
]);
}
const camera = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["styles", [_style_0]], ["__file", "D:/项目/hldy_app_mini/pages/camera.nvue"]]);
const camera = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["styles", [_style_0]], ["__file", "D:/hldy_app_mini/pages/camera.nvue"]]);
export {
camera as default
};

View File

@ -577,7 +577,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
)
]);
}
const fullcamera = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["styles", [_style_0]], ["__file", "D:/项目/hldy_app_mini/pages/fullcamera.nvue"]]);
const fullcamera = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["styles", [_style_0]], ["__file", "D:/hldy_app_mini/pages/fullcamera.nvue"]]);
export {
fullcamera as default
};