h5
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

480 lines
14 KiB

<template>
<view>
<scroll-view scroll-y="true">
<view v-if="!data.changeEvent">
<view :id="'cu-' + index" :key="item.id" class="cu-item flex-col" v-for="(item, index) in data.itemList">
<ProjectItem
class="w-full"
:index="index"
:item="item"
:menuList="data.menuList"
@setData="setData"
@openSubProject="openSubProject"
/>
</view>
</view>
<view v-else>
<view
:id="'cu-' + index"
:key="index"
:style="{ 'background-color': item.color }"
@touchend="stops($event, index)"
@touchmove.stop.prevent="move"
@touchstart="start($event, index)"
class="cu-item flex-col" v-for="(item, index) in data.itemList"
>
<view class="border-100 bg-blue-500" v-if="item.showTopBorder"></view>
<!-- 内容区 -->
<!-- 父项目 -->
<view class="w-full">
<view class="flex items-center justify-between p-3">
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon>
<view class="flex-1 px-3">
<view class="flex items-center mb-1">
<view class="mr-2">{{ item.name }}</view>
<!-- 状态 TODO:-->
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view>
</view>
<view class="flex items-center text-xs text-gray-400">
<view class="pr-2">{{ dayjs(+item.startTime).format('MM-DD HH:mm') }}</view>
<view class="pl-2">{{ dayjs(+item.endTime).format('MM-DD HH:mm') }}</view>
</view>
</view>
<!-- 箭头 -->
<view v-if="item.sonProjectList && item.sonProjectList.length">
<u-icon @click="openSubProject(item.sonProjectList.length, index)" class="text-gray-400" name="arrow-up" size="14px" v-if="item.show"></u-icon>
<u-icon @click="openSubProject(item.sonProjectList.length, index)" class="text-gray-400" name="arrow-down" size="14px" v-else></u-icon>
</view>
<u-icon class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon>
</view>
<!-- 父项目 end -->
<!-- 子项目 -->
<view class="ml-8" v-if="item.show">
<view :id="'cu-' + index + '-' + subIndex" :key="subIndex"
@touchend.stop.prevent="stops($event, index + '-' + subIndex, item.sonProjectList.length)"
@touchmove.stop.prevent="move($event, item.sonProjectList.length)"
@touchstart.stop.prevent="start($event, index + '-' + subIndex)"
class="cu-item flex-col" v-for="(subItem, subIndex) in item.sonProjectList">
<view class="flex items-center justify-between p-3 w-full">
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48">
</u-icon>
<view class="flex-1 px-3">
<view class="flex items-center">
<view class="mr-2">{{ subItem.name }}</view>
<!-- 状态 -->
<view
:class="
subItem.status === 0
? 'text-blue-400 bg-blue-100'
: subItem.status === 1
? 'text-green-400 bg-green-100'
: subItem.status === 2
? 'text-red-400 bg-red-100'
: 'text-gray-400 bg-gray-100'
"
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0">
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }}
</view>
</view>
</view>
<!-- 箭头 -->
<u-icon class="text-gray-400" name="arrow-right" size="14px"></u-icon>
</view>
</view>
</view>
</view>
<!-- 内容区 end -->
<view class="border-100 bg-blue-500" v-if="item.showBorder"></view>
<view class="border-80 bg-blue-500" v-if="item.showSubBorder"></view>
</view>
</view>
</scroll-view>
<!-- 移动悬浮 begin -->
<view v-if="data.showMoveImage">
<view :style="{ left: moveLeft + 'px', top: moveTop + 'px' }" class="cu-item absolute">
<ProjectItem class="w-full" :item="moveItem" />
</view>
</view>
<!-- 移动悬浮 end -->
<!-- 项目操作面板 -->
<u-action-sheet :list="data.menuList" :tips="data.tips" @click="chooseAction" v-model="data.showMenu"></u-action-sheet>
</view>
</template>
<script setup>
import { ref, onMounted, watch, computed } from 'vue';
import ProjectItem from '@/components/Projects/ProjectItem.vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
const store = useStore();
const projects = computed(() => store.state.project.projects);
const data = ref({
itemTop: 0,
itemLeft: 0,
itemHeight: 0, // 移动元素的高度
subItemHeight: 0, // 移动子元素的高度
itemWidth: 0, // 移动元素的宽度
showMoveImage: false,
moveItem: '',
moveLeft: 0,
moveTop: 0,
deltaLeft: 0,
deltaTop: 0,
beginleft: 0,
begintop: 0,
itemList: [],
setSubItem: false,
changeEvent: false,
showMenu: false,
tips: {
text: '',
color: '#909399',
fontSize: 28,
},
projectId: 0,
menuList: [{ text: '复制' }, { text: '编辑' }, { text: '删除' }, { text: '置顶' }, { text: '排序' }],
// show: false,
border: 'border border-blue-500 shadow rounded-md',
showBorder: false,
showItemIndex: undefined,
});
watch(projects, (val) => {
data.value.itemList = val;
data.value.itemList.forEach(item => {
item.showBorder = false;
item.showSubBorder = false;
item.showTopBorder = false;
});
})
onMounted(() => {
data.value.itemList = projects.value;
data.value.itemList.forEach(item => {
item.showBorder = false;
item.showSubBorder = false;
item.showTopBorder = false;
});
});
// 展开子项目
function openSubProject(length, index) {
setProjectItemShow({ index, show: data.value.itemList[index].show ? false : true });
if (length && index) {
this.$emit('changeHeight', length, index);
}
data.value.showItemIndex = index;
}
// 获取项目列表距离顶部的距离
function getDate() {
const query = uni.createSelectorQuery().in(this);
query.select(`#cu-0`).boundingClientRect(res => {
console.log('data: ', res);
data.value.begintop = res.top;
data.value.beginleft = res.left;
}).exec();
}
function setData(flag, projectId, tips) {
data.value.showMenu = flag;
data.value.projectId = projectId;
data.value.tips = tips;
}
function chooseAction(e) {
let obj = { index: e, projectId: data.value.projectId };
// this.$emit('chooseAction', data);
actionFun(obj);
}
// 操作
function actionFun(obj) {
let action = data.value.menuList[obj.index].text;
if (action === '排序') {
data.value.changeEvent = true;
uni.$ui.showToast('请移动进行排序');
}
if (action === '删除') {
data.value.changeEvent = false;
delProject(obj.projectId);
}
if (data.value.showItemIndex !== undefined) {
setProjectItemShow({ index: data.value.showItemIndex, show: true });
}
}
function isNumber(val) {
return val === +val;
}
function start(e, index) {
console.log('开始', e);
setTimeout(() => {
getDate();
}, 300);
if (isNumber(index)) {
data.value.setSubItem = false;
const query = uni.createSelectorQuery().in(this);
console.log('2222', query)
query.select(`#cu-${index}`).boundingClientRect(res => {
data.value.moveTop = res.top;
data.value.moveLeft = res.left;
data.value.moveItem = data.value.itemList[index];
data.value.itemWidth = res.width;
data.value.itemHeight = res.height;
}).exec();
} else {
let arr = index.split('-');
data.value.setSubItem = true;
const query = uni.createSelectorQuery().in(this);
query.select(`#cu-${arr[0] - 0}`).boundingClientRect(res => {
data.value.itemHeight = res.height;
}).exec();
query.select(`#cu-${index}`).boundingClientRect(res => {
data.value.moveTop = res.top;
data.value.moveLeft = res.left;
data.value.moveItem = data.value.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
data.value.itemWidth = res.width;
data.value.subItemHeight = res.height;
}).exec();
}
}
function move(e, length) {
console.log('移动');
data.value.showMoveImage = true; //悬浮开始
const touch = e.touches[0];
if (data.value.deltaLeft == 0) {
// 获得本身的移动
data.value.deltaLeft = touch.pageX - data.value.moveLeft;
data.value.deltaTop = touch.pageY - data.value.moveTop;
}
data.value.moveLeft = touch.pageX - data.value.deltaLeft;
data.value.moveTop = touch.pageY - data.value.deltaTop;
let lastIndex = (lastIndex = findOverIndex(touch.pageY, length));
console.log('111111', lastIndex);
// 显示下划线
for (let i = 0; i < data.value.itemList.length; i++) {
if (data.value.moveLeft > 35) {
data.value.itemList[i].showBorder = false;
data.value.itemList[i].showTopBorder = false;
if (i === lastIndex) {
data.value.itemList[i].showSubBorder = true;
} else {
data.value.itemList[i].showSubBorder = false;
}
} else {
if (lastIndex === -1) {
data.value.itemList[0].showTopBorder = true;
data.value.itemList[i].showSubBorder = false;
data.value.itemList[i].showBorder = false;
} else {
data.value.itemList[i].showSubBorder = false;
data.value.itemList[i].showTopBorder = false;
if (i === lastIndex) {
data.value.itemList[i].showBorder = true;
} else {
data.value.itemList[i].showBorder = false;
}
}
}
}
}
function stops(e, index, length) {
console.log('结束');
const touch = e.mp.changedTouches[0];
let lastIndex = (lastIndex = findOverIndex(touch.pageY, length));
// 交换两个值
for (let i = 0; i < data.value.itemList.length; i++) {
// 插入顶部
if (data.value.itemList[i].showTopBorder) {
if (isNumber(index)) {
let Value = data.value.itemList[index];
data.value.itemList.unshift(Value);
data.value.itemList.splice(index + 1, 1);
} else {
let arr = index.split('-');
let Value = data.value.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
data.value.itemList.unshift(Value);
data.value.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1);
const options = {
id: Value.id,
parentId: 0,
};
this.$emit('change', options);
}
// 清空
clearSet(i);
this.$emit('change', data.value.itemList);
return;
}
// 插入一级项目
if (data.value.itemList[i].showBorder) {
if (isNumber(index)) {
let Value = data.value.itemList[index];
data.value.itemList.splice(i + 1, 0, Value);
if (i < index) {
data.value.itemList.splice(index + 1, 1);
} else {
data.value.itemList.splice(index, 1);
}
} else {
let arr = index.split('-');
let Value = data.value.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
data.value.itemList.splice(i + 1, 0, Value);
data.value.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1);
const options = {
id: Value.id,
parentId: 0,
};
this.$emit('change', options);
}
// 清空
clearSet(i);
this.$emit('change', data.value.itemList);
return;
}
// 插入二级项目
if (data.value.itemList[i].showSubBorder) {
if (isNumber(index)) {
let Value = data.value.itemList[index];
if (data.value.itemList[lastIndex - 1].sonProjectList && data.value.itemList[lastIndex - 1].sonProjectList.length) {
data.value.itemList[lastIndex - 1].sonProjectList.push(Value);
} else {
data.value.itemList[lastIndex].sonProjectList = [Value];
}
data.value.itemList.splice(index, 1);
// 清空
clearSet(i);
const options = {
id: Value.id,
parentId: data.value.itemList[lastIndex - 1].id,
};
this.$emit('change', options);
} else {
let arr = index.split('-');
let Value = data.value.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
if (data.value.itemList[lastIndex].sonProjectList && data.value.itemList[lastIndex].sonProjectList.length) {
data.value.itemList[lastIndex].sonProjectList.push(Value);
} else {
data.value.itemList[lastIndex].sonProjectList = [Value];
}
data.value.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1);
// 清空
clearSet(i);
const options = {
id: Value.id,
parentId: data.value.itemList[lastIndex].id,
};
this.$emit('change', options);
const options1 = {
id: Value.id,
parentId: 0,
};
this.$emit('change', options1);
}
return;
}
}
}
// 还原初始数据
function clearSet(i) {
data.value.itemList[i].showBorder = false;
data.value.itemList[i].showSubBorder = false;
data.value.itemList[i].showTopBorder = false;
data.value.deltaLeft == 0;
data.value.showMoveImage = false;
data.value.setSubItem = false;
data.value.changeEvent = false;
data.value.showItemIndex = undefined;
}
// 找到停下的元素的下标
function findOverIndex(posY) {
// 如果有子项目展开着
let leng = data.value.itemList.length * data.value.itemHeight; // 最后一个元素距离顶部的距离
if (posY < data.value.begintop) {
return -1;
}
for (var i = 0; i < data.value.itemList.length; i++) {
let begin = data.value.itemHeight * i + data.value.begintop;
let end = data.value.itemHeight * i + data.value.begintop + data.value.itemHeight;
if (begin <= posY && end >= posY) {
return i;
}
}
if (posY > leng) {
// 交换最后一个
return data.value.itemList.length - 1;
} else if (posY < data.value.begintop) {
return 0;
}
}
// 删除项目
function delProject(id) {
uni.showModal({
title: '',
content: '是否删除项目?',
showCancel: true,
success: async ({ confirm }) => {
if (confirm) {
await this.$u.api.delProject(id);
let flag_index = 0;
data.value.itemList.forEach((item, index) => {
if (item.id == id) {
flag_index = index;
}
});
data.value.itemList.splice(flag_index, 1);
setProjects(data.value.itemList);
}
},
});
}
</script>
<style lang="scss" scoped>
.cu-item {
width: 100%;
display: flex;
align-items: center;
font-size: 14px;
}
.border-100 {
width: 92%;
height: 4rpx;
}
.border-80 {
width: 84%;
height: 2px;
margin-left: 30px;
}
</style>