iZaiZaiA 2 роки тому
батько
коміт
e7ae182c8d

+ 3 - 5
src/views/ledger/components/table-form.vue

@@ -175,10 +175,8 @@ nextTick(() => {
 const getQueryData = () => {
     const {excelId} = menuItem.value
     excelIdVal.value = excelId > 0 ? excelId + '' : ''
-    const date = recordDate.value, time = recordTime.value
-    if (!getObjNullValue(date) && time) {
-        getExcelBusinessData(date,time)
-    }
+    const date = recordDate.value ?? {}, time = recordTime.value ?? ''
+    getExcelBusinessData(date,time)
 }
 
 //日期日历回调
@@ -296,7 +294,7 @@ const getBussDataInfo = (index = 0) => {
 //获取日期记录
 const dateData = ref([])
 const getSubmitLogDateList = async ({year},pid) => {
-    if (pid > 0) {
+    if (pid > 0 && year) {
         const { data } = await queryApi.getSubmitLogDateList({
             projectId: projectId.value,
             contractId: contractId.value,

+ 209 - 0
src/views/tentative/collect/components/HcTreeData.vue

@@ -0,0 +1,209 @@
+<template>
+    <ElTree class="hc-tree-node tree-line" ref="ElTreeRef" :props="ElTreeProps" :data="datas" highlight-current accordion node-key="primaryKeyId"
+            :default-expanded-keys="TreeExpandKey" @node-click="ElTreeClick" @node-contextmenu="ElTreeLabelContextMenu" :indent="0">
+        <template #default="{ node, data }">
+            <div class="data-custom-tree-node" :id="`${idPrefix}${data['primaryKeyId']}`">
+                <!--树组件,节点名称-->
+                <div class="label" :class="node.level === 1?'level-name':''">
+                    <span :class="data?.colorStatus === 2?'text-blue':data?.colorStatus === 3?'text-orange':data?.colorStatus === 4?'text-green':''" v-if="isColor">{{ node.label }}</span>
+                    <span v-else>{{ node.label }}</span>
+                </div>
+                <!--树组件,操作菜单-->
+                <div class="menu-icon" :class="node.showTreeMenu?'show':''" v-if="node.level !== 1 && menusData.length > 0" @click.stop>
+                    <div class="cu-tree-node-popover-menu-icon" @click.prevent.stop="ElTreeLabelContextMenu($event,data,node)">
+                        <HcIcon name="menu" ui="text-2xl"/>
+                    </div>
+                </div>
+                <!--树组件,操作菜单 END-->
+            </div>
+        </template>
+    </ElTree>
+    <!--右键菜单-->
+    <HcContextMenu ref="contextMenuRef" :datas="menusData" @item-click="handleMenuSelect" v-if="menusData.length > 0">
+        <template #mark="{item}">
+            <HcIcon :name="item.icon" :fill="treeRefData?.isFirst" class="menu-item-icon"/>
+            <span class="menu-item-name">{{treeRefData?.isFirst ? '取消标记为首件' : '标记为首件'}}</span>
+        </template>
+        <template #sort="{item}">
+            <HcIcon :name="item.icon" :line="false" class="menu-item-icon"/>
+            <span class="menu-item-name">{{item.label}}</span>
+        </template>
+    </HcContextMenu>
+</template>
+
+<script setup>
+import {ref,watch} from "vue";
+import {getArrValue,getObjValue} from "vue-utils-plus"
+//参数
+const props = defineProps({
+    menus: {
+        type: Array,
+        default: () => ([])
+    },
+    datas: {
+        type: Array,
+        default: () => ([])
+    },
+    autoExpandKeys: {
+        type: Array,
+        default: () => ([])
+    },
+    isMark: {
+        type: Boolean,
+        default: false
+    },
+    idPrefix: {
+        type: String,
+        default: 'tree-data-'
+    },
+    isAutoKeys: {
+        type: Boolean,
+        default: true
+    },
+    isAutoClick: {
+        type: Boolean,
+        default: true
+    },
+    isColor: {
+        type: Boolean,
+        default: false
+    },
+})
+
+//变量
+const ElTreeRef = ref(null)
+const treeRefNode = ref(null)
+const treeRefData = ref(null)
+const ElTreeProps = ref({
+    label: 'title',
+    children: 'children',
+    isLeaf: 'notExsitChild'
+})
+const menusData = ref(props.menus)
+const menuMark = ref(props.isMark)
+const isAutoKeys = ref(props.isAutoKeys)
+const TreeExpandKey = ref(props.autoExpandKeys)
+const idPrefix = ref(props.idPrefix);
+
+//监听
+watch(() => [
+    props.menus,
+    props.isMark,
+    props.isAutoKeys,
+    props.autoExpandKeys,
+    props.idPrefix,
+], ([menus, isMark, AutoKeys, expandKeys, UserIdPrefix]) => {
+    menusData.value = menus
+    menuMark.value = isMark
+    isAutoKeys.value = AutoKeys
+    TreeExpandKey.value = expandKeys
+    idPrefix.value = UserIdPrefix
+})
+
+//事件
+const emit = defineEmits(['menuTap','nodeTap'])
+
+//节点被点击
+const ElTreeClick = async (data,node) => {
+    if (isAutoKeys.value) {
+        let autoKeysArr = []
+        await getNodeExpandKeys(node, autoKeysArr)
+        const autoKeys = autoKeysArr.reverse()
+        emit('nodeTap', {node, data, keys: autoKeys})
+    } else {
+        emit('nodeTap', {node, data, keys: []})
+    }
+}
+
+//处理自动展开的节点KEY
+const getNodeExpandKeys = async (node, newKeys) => {
+    const parent = getArrValue(node?.parent)
+    const nodeData = getObjValue(node?.data);
+    const primaryKeyId = nodeData?.primaryKeyId ?? ''
+    if (primaryKeyId) {
+        newKeys.push(primaryKeyId)
+        await getNodeExpandKeys(parent, newKeys)
+    }
+}
+
+//鼠标右键事件
+const contextMenuRef = ref(null)
+const ElTreeLabelContextMenu = (e,data,node) => {
+    const rows = menusData.value || [];
+    if (node.level !== 1 && rows.length > 0) {
+        e.preventDefault();
+        treeRefNode.value = node;
+        treeRefData.value = data;
+        //展开菜单
+        contextMenuRef.value?.showMenu(e)
+    }
+}
+
+//鼠标右键菜单被点击
+const handleMenuSelect = ({key}) => {
+    const node = treeRefNode.value;
+    const data = treeRefData.value;
+    //如果为标记菜单
+    if (key === 'mark' && menuMark.value) {
+        if (data.isFirst === true) {
+            emit('menuTap', {key: 'cancel_mark', node, data})
+        } else {
+            emit('menuTap', {key: 'mark', node, data})
+        }
+    } else {
+        emit('menuTap', {key, node, data})
+    }
+}
+//设置树菜单的标记数据
+const setElTreeMenuMark = (keys,isFirst) => {
+    keys.forEach(item => {
+        //根据 data 或者 key 拿到 Tree 组件中的 node
+        let node = ElTreeRef.value.getNode(item)
+        if (!!node) node.data.isFirst = isFirst;
+    })
+}
+
+//设置树菜单的标记数据
+const removeElTreeNode = (key) => {
+    //根据 data 或者 key 拿到 Tree 组件中的 node
+    let node = ElTreeRef.value.getNode(key)
+    //删除 Tree 中的一个节点,使用此方法必须设置 node-key 属性
+    ElTreeRef.value.remove(node)
+}
+
+// 暴露出去
+defineExpose({
+    setElTreeMenuMark,
+    removeElTreeNode
+})
+</script>
+
+<style lang="scss" scoped>
+@import "../../../styles/app/tree.scss";
+.data-custom-tree-node {
+    .menu-icon {
+        position: relative;
+        font-size: 20px;
+        opacity: 0;
+        pointer-events: none;
+        transition: opacity 0.2s;
+        .cu-tree-node-popover-menu-icon {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+        }
+    }
+    &:hover {
+        .menu-icon {
+            opacity: 1;
+            pointer-events: all;
+            cursor: context-menu;
+        }
+    }
+    .menu-icon.show {
+        opacity: 1;
+        pointer-events: all;
+        cursor: context-menu;
+    }
+}
+</style>

+ 137 - 0
src/views/tentative/collect/components/HcTreeNode.vue

@@ -0,0 +1,137 @@
+<template>
+    <ElTree class="hc-tree-node-box hc-tree-node tree-line" ref="ElTreeRef" :props="ElTreeProps" :data="ElTreeData" :node-key="nodeKey" highlight-current accordion show-checkbox
+            :check-strictly="isStrictly" :expand-on-click-node="false" @check="ElTreeCheckChange" :indent="0">
+        <template #default="{ node, data }">
+            <div class="custom-tree-node">
+                <div class="label" @dblclick="ElTreeDblClick(data)">
+                    <el-input v-model="data.title" size="small" @keyup="keyUpEvent($event,data)" v-if="data.isInputName" @blur="ElTreeBtnClick(data)">
+                        <template #append>
+                            <el-button type="primary" plain size="small" @click="ElTreeBtnClick(data)">
+                                <HcIcon name="check"/>
+                            </el-button>
+                        </template>
+                    </el-input>
+                    <span v-else>{{data.title}}</span>
+                </div>
+            </div>
+        </template>
+    </ElTree>
+</template>
+
+<script setup>
+import {nextTick, onMounted, ref, watch} from "vue";
+import {getArrValue} from "vue-utils-plus"
+import wbsApi from '~api/data-fill/wbs';
+
+//参数
+const props = defineProps({
+    projectId: {
+        type: [String,Number],
+        default: ''
+    },
+    nodeKey: {
+        type: String,
+        default: 'primaryKeyId'
+    },
+    nodeId: {
+        type: [String,Number],
+        default: ''
+    },
+    oldId: {
+        type: [String,Number],
+        default: ''
+    },
+    strictly: {
+        type: Boolean,
+        default: false
+    },
+})
+
+//变量
+const ElTreeRef = ref(null)
+const projectId = ref(props.projectId)
+const isStrictly = ref(props.strictly)
+const ElTreeProps = ref({label: 'title', children: 'children', isLeaf: 'notExsitChild'})
+
+//监听
+watch(() => [
+    props.projectId,
+    props.strictly,
+], ([pid,strictly]) => {
+    projectId.value = pid
+    isStrictly.value = strictly
+})
+
+//渲染完成
+onMounted(() => {
+    ElTreeLoadNode()
+})
+
+//事件
+const emit = defineEmits(['check-change'])
+
+//树形结构异步加载数据
+const ElTreeData = ref([])
+const ElTreeLoadNode = async () => {
+    let nodeId = props.oldId || props.nodeId || ''
+    const {error, code, data} = await wbsApi.queryWbsTreePrivateByProjectIdAndId({
+        id: nodeId,
+        projectId: projectId.value
+    })
+    if (!error && code === 200) {
+        ElTreeData.value = getArrValue(data)
+        await nextTick(() => {
+            ElTreeCheckedKeys()
+        })
+    }
+}
+
+//被选择的
+const ElTreeCheckChange = (_,nodes) => {
+    emit('check-change', nodes)
+}
+
+//处理节点
+const ElTreeCheckedKeys = () => {
+    const Nodes = ElTreeRef.value.getCheckedNodes() || []
+    const HalfNodes = ElTreeRef.value.getHalfCheckedNodes() || []
+    emit('check-change', {
+        checkedNodes: Nodes,
+        halfCheckedNodes: HalfNodes
+    })
+}
+
+//更改节点名称
+const ElTreeDblClick = (item) => {
+    item.isInputName = true;
+}
+//回车
+const keyUpEvent = (e,item) => {
+    if (e.key === "Enter") {
+        ElTreeBtnClick(item)
+    }
+}
+//更改节点名称完成
+const ElTreeBtnClick = (item) => {
+    if (!item?.title) {
+        window?.$message?.warning('节点名称不能为空')
+    } else {
+        item.isInputName = false;
+        ElTreeCheckedKeys()
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "../../../styles/app/tree.scss";
+.custom-tree-node {
+    position: relative;
+    width: 100%;
+}
+</style>
+
+<style lang="scss">
+.hc-tree-node-box .el-tree-node__content {
+    height: 32px !important;
+}
+</style>

+ 85 - 0
src/views/tentative/collect/components/TestTree.vue

@@ -0,0 +1,85 @@
+<template>
+    <ElTree class="hc-tree-node tree-line" :class="ui" ref="ElTreeRef" :props="ElTreeProps" :load="ElTreeLoadNode" lazy show-checkbox accordion node-key="primaryKeyId"
+            :default-expanded-keys="defaultExpandedCids" :indent="0" @check-change="ElTreeCheckChange">
+    </ElTree>
+</template>
+
+<script setup>
+import {ref,watch} from "vue";
+import dataFillQuery from '~api/data-fill/query';
+import {getArrValue,getObjValue} from "vue-utils-plus"
+
+//参数
+const props = defineProps({
+    projectId: {
+        type: [String,Number],
+        default: ''
+    },
+    contractId: {
+        type: [String,Number],
+        default: ''
+    },
+    ui: {
+        type: String,
+        default: ''
+    },
+})
+
+//变量
+const ElTreeRef = ref(null)
+const ElTreeProps = ref({
+    label: 'title',
+    children: 'children',
+    isLeaf: 'notExsitChild'
+})
+const projectId = ref(props.projectId);
+const contractId = ref(props.contractId);
+
+//监听
+watch(() => [
+    props.projectId,
+    props.contractId
+], ([UserProjectId, UserContractId]) => {
+    projectId.value = UserProjectId
+    contractId.value = UserContractId
+})
+
+//事件
+const emit = defineEmits(['change'])
+
+//树形结构异步加载数据
+const defaultExpandedCids = ref([])
+const ElTreeLoadNode = async (node, resolve) => {
+    let defaultExpandedArr = [];
+    let contractIdRelation = '', parentId = '', primaryKeyId = '';
+    if (node.level !== 0) {
+        const nodeData = getObjValue(node?.data);
+        contractIdRelation = nodeData?.contractIdRelation || ''
+        parentId = contractIdRelation ? nodeData?.primaryKeyId : nodeData?.id
+        primaryKeyId = nodeData?.id || ''
+    }
+    //获取数据
+    const { data } = await dataFillQuery.queryWbsTreeData({
+        contractId: contractId.value || '',
+        contractIdRelation,
+        primaryKeyId,
+        parentId
+    })
+    const resData = getArrValue(data)
+    if (resData.length > 0 && node.level === 0) {
+        defaultExpandedArr.push(resData[0]?.primaryKeyId)
+    }
+    //自动展开
+    defaultExpandedCids.value = defaultExpandedArr
+    resolve(resData)
+}
+
+//节点勾选
+const ElTreeCheckChange = (data, checked, indeterminate) => {
+    emit('change', {data, checked, indeterminate})
+}
+</script>
+
+<style lang="scss" scoped>
+@import "../../../../styles/app/tree.scss";
+</style>

+ 287 - 6
src/views/tentative/collect/monthly.vue

@@ -1,24 +1,245 @@
 <template>
-    <div class="hc-layout-box">
-        月报汇总
+    <div class="hc-page-layout-box">
+        <div class="hc-layout-left-box" :style="'width:' + leftWidth + 'px;'">
+            <div class="hc-project-box">
+                <div class="hc-project-icon-box">
+                    <HcIcon name="stack"/>
+                </div>
+                <div class="ml-2 project-name-box">
+                    <span class="text-xl text-cut project-alias">{{projectInfo['projectAlias']}}</span>
+                    <div class="text-xs text-cut project-name">{{projectInfo['name']}}</div>
+                </div>
+            </div>
+            <div class="hc-tree-box">
+                <el-scrollbar>
+                    <TestTree :projectId="projectId" :contractId="contractId" @change="testTreeCheckChange"/>
+                </el-scrollbar>
+            </div>
+            <!--左右拖动-->
+            <div class="horizontal-drag-line" @mousedown="onmousedown"/>
+        </div>
+        <div class="hc-page-content-box">
+            <HcCard>
+                <template #header>
+                    <div class="w-40">
+                        <el-select v-model="searchForm.type" placeholder="请选择材料类别" clearable>
+                            <el-option v-for="item in typeData" :key="item.value" :label="item['label']" :value="item['value']"/>
+                        </el-select>
+                    </div>
+                    <div class="w-64 ml-2">
+                        <HcDatePicker :dates="betweenTime" clearable @change="betweenTimeUpdate"/>
+                    </div>
+                    <div class="ml-2">
+                        <el-button type="primary" @click="searchClick">
+                            <HcIcon name="search-2"/>
+                            <span>搜索</span>
+                        </el-button>
+                    </div>
+                </template>
+                <template #extra>
+                    <HcTooltip keys="tentative_material_approach_add">
+                        <el-button type="primary" hc-btn>
+                            <HcIcon name="send-plane-2"/>
+                            <span>上报</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="tentative_material_approach_printer">
+                        <el-button hc-btn>
+                            <HcIcon name="printer"/>
+                            <span>打印</span>
+                        </el-button>
+                    </HcTooltip>
+                    <HcTooltip keys="tentative_material_approach_import">
+                        <el-button hc-btn>
+                            <HcIcon name="download"/>
+                            <span>下载</span>
+                        </el-button>
+                    </HcTooltip>
+                </template>
+                <div class="hc-table-ref-box">
+                    <el-table class="hc-table-diy-box" :data="tableData" :span-method="tableSpanMethod" height="100%" style="width: 100%">
+                        <el-table-column label="试验组数" width="300" align="right">
+                            <el-table-column prop="title" label="试验项目" width="220" align="left"></el-table-column>
+                            <el-table-column prop="month" label="月" align="center" width="80"></el-table-column>
+                        </el-table-column>
+                        <el-table-column label="承包人" align="center">
+                            <el-table-column prop="name" label="合格数" align="center"></el-table-column>
+                            <el-table-column prop="state" label="不合格树" align="center"></el-table-column>
+                            <el-table-column prop="zip" label="备注" align="center"></el-table-column>
+                        </el-table-column>
+                        <el-table-column label="操作" width="140" align="center">
+                            <template #default="scope">
+                                <!--scope.row scope.$index-->
+                                <HcTooltip keys="tentative_material_approach_annex">
+                                    <el-button type="primary" size="small" plain>编辑备注</el-button>
+                                </HcTooltip>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </div>
+                <template #action>
+                    <HcPages :pages="searchForm" @change="pageChange"/>
+                </template>
+            </HcCard>
+        </div>
     </div>
 </template>
 
 <script setup>
-import {ref,watch,onMounted} from "vue";
-import {useRouter, useRoute} from 'vue-router'
+import {ref, onMounted, watch} from "vue";
 import {useAppStore} from "~src/store";
+import {useRouter, useRoute} from 'vue-router'
+import TestTree from "./components/TestTree.vue"
 
 //初始变量
 const router = useRouter()
 const useRoutes = useRoute()
 const useAppState = useAppStore()
-//const {getObjValue, getArrValue} = isType()
 
 //全局变量
 const projectId = ref(useAppState.getProjectId);
 const contractId = ref(useAppState.getContractId);
+const projectInfo = ref(useAppState.getProjectInfo);
+const isCollapse = ref(useAppState.getCollapse)
+
+//监听
+watch(() => [
+    useAppState.getCollapse
+], ([Collapse]) => {
+    isCollapse.value = Collapse
+})
+
+//渲染完成
+onMounted(() => {
+
+})
+
+//树被勾选
+const nodeItemInfo = ref({})
+const testTreeCheckChange = ({data, checked, indeterminate}) => {
+    console.log(data, checked, indeterminate)
+    //nodeItemInfo.value = data
+    //searchForm.value.current = 1;
+    //getTableData()
+}
+
+//搜索表单
+const searchForm = ref({
+    type: null, approval: null, betweenTime: null,
+    current: 1, size: 20, total: 0
+})
+
+//检测类别
+const typeData = ref([
+    {label: '自检', value: '1'},
+    {label: '抽检', value: '2'},
+    {label: '平行试验', value: '3'},
+    {label: '验证试验', value: '4'},
+    {label: '中心试验', value: '5'}
+])
 
+//日期时间被选择
+const betweenTime = ref(null)
+const betweenTimeUpdate = ({arr,query}) => {
+    betweenTime.value = arr
+    searchForm.value.betweenTime = query
+}
+
+//搜索
+const searchClick = () => {
+    searchForm.value.current = 1;
+    getTableData()
+}
+
+//分页被点击
+const pageChange = ({current, size}) => {
+    searchForm.value.current = current
+    searchForm.value.size = size
+    getTableData()
+}
+
+//表格合并
+const tableSpanMethod = ({rowIndex, columnIndex}) => {
+    if (columnIndex === 0) {
+        if (rowIndex % 2 === 0) {
+            return {
+                rowspan: 2,
+                colspan: 1,
+            }
+        } else {
+            return {
+                rowspan: 0,
+                colspan: 0,
+            }
+        }
+    }
+}
+
+//获取数据
+const tableLoading = ref(false)
+const tableData = ref([
+    {
+        title: 'No. 189, Grove St, Los Angeles',
+        month: '本月',
+        name: '100',
+        state: '2',
+        zip: 'CA 90036',
+    },
+    {
+        title: 'No. 189, Grove St, Los Angeles',
+        month: '累计',
+        name: '100',
+        state: '2',
+        zip: 'CA 90036',
+    },
+    {
+        title: 'No. 189, Grove St, Los Angeles',
+        month: '本月',
+        name: '100',
+        state: '2',
+        zip: 'CA 90036',
+    },
+    {
+        title: 'No. 189, Grove St, Los Angeles',
+        month: '累计',
+        name: '100',
+        state: '2',
+        zip: 'CA 90036',
+    },
+    {
+        title: 'No. 189, Grove St, Los Angeles',
+        month: '本月',
+        name: '100',
+        state: '2',
+        zip: 'CA 90036',
+    },
+    {
+        title: 'No. 189, Grove St, Los Angeles',
+        month: '累计',
+        name: '100',
+        state: '2',
+        zip: 'CA 90036',
+    },
+])
+const getTableData = () => {
+
+}
+
+//左右拖动,改变树形结构宽度
+const leftWidth = ref(382);
+const onmousedown = () => {
+    const leftNum = isCollapse.value ? 142 : 272
+    document.onmousemove = (ve) => {
+        let diffVal = ve.clientX - leftNum;
+        if(diffVal >= 310 && diffVal <= 900) {
+            leftWidth.value = diffVal;
+        }
+    }
+    document.onmouseup = () => {
+        document.onmousemove = null;
+        document.onmouseup = null;
+    }
+}
 </script>
 
 <style lang="scss" scoped>
@@ -26,5 +247,65 @@ const contractId = ref(useAppState.getContractId);
 </style>
 
 <style lang="scss">
-
+.hc-table-ref-box {
+    height: 100%;
+    .el-table.hc-table-diy-box {
+        --el-table-bg-color: initial;
+        --el-table-header-bg-color: #DAE8F3;
+        --el-table-header-text-color: #50545E;
+        --el-table-tr-bg-color: #F1F5F8;
+        --el-fill-color-light: #e7ebee;
+        --el-table-text-color: #666666;
+        --el-table-row-hover-bg-color: var(--el-color-primary-light-9);
+        &.el-table--border {
+            --el-table-border-color: #d7d7d7;
+        }
+        thead.is-group {
+            background: none;
+            //表头字体
+            tr:first-of-type th:first-of-type,
+            tr:first-of-type th:first-of-type {
+                .cell {
+                    display: inline-flex;
+                    font-size: 16px;
+                    line-height: initial;
+                    position: relative;
+                }
+            }
+            //表头其他样式
+            tr:first-of-type th:first-of-type {
+                border-bottom: none;
+                .cell {
+                    top: 7px;
+                }
+            }
+            tr:last-of-type th:first-of-type {
+                border-right: 0;
+                .cell {
+                    top: -7px;
+                }
+            }
+            //斜线
+            tr:last-of-type th:nth-of-type(2) {
+                .cell {
+                    visibility: hidden;
+                    line-height: 0;
+                    padding: 0;
+                }
+                &::before, {
+                    content: '';
+                    position: absolute;
+                    width: 1px;
+                    height: 308px;
+                    background-color: #d7d7d7;
+                    display: block;
+                    bottom: 0;
+                    right: 0;
+                    transform: rotate(-75.5deg);
+                    transform-origin: bottom;
+                }
+            }
+        }
+    }
+}
 </style>