yxhc пре 1 месец
родитељ
комит
8a24bc6645
16 измењених фајлова са 1795 додато и 0 уклоњено
  1. +65
    -0
      src/components/accessory/index.vue
  2. +64
    -0
      src/components/breadcrumb/index.vue
  3. +108
    -0
      src/components/elAside/index.vue
  4. +92
    -0
      src/components/elTree/index.vue
  5. +27
    -0
      src/components/fileView/index.vue
  6. +267
    -0
      src/components/indexTree/index.vue
  7. +9
    -0
      src/components/inputRangeNum/index.vue
  8. +337
    -0
      src/components/layout/index.vue
  9. +32
    -0
      src/components/noData/index.vue
  10. +324
    -0
      src/components/orgTree/index.vue
  11. +43
    -0
      src/components/sidebar/index.vue
  12. +79
    -0
      src/components/sidebar/sidebarItem.vue
  13. +27
    -0
      src/components/svgIcon/index.vue
  14. +16
    -0
      src/components/tableList/E-expand.js
  15. +243
    -0
      src/components/tableList/index.vue
  16. +62
    -0
      src/components/tagsView/index.vue

+ 65
- 0
src/components/accessory/index.vue Прегледај датотеку

@@ -0,0 +1,65 @@
<script setup name="accessory">
import { downFileById } from '@/http/apis/commonApi'
import { useRouter } from 'vue-router'

const props = defineProps({
fileName: {
type: String,
default: ''
},
fileId: {
type: Number,
default: undefined
},
isDownLoad: {
type: Boolean,
default: true
}
})
// 下载附件
const downloadFile = async () => {
const res = await downFileById({ fileId: props.fileId })
const url = URL.createObjectURL(res)
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.download = props.fileName
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
}
// 预览附件
const router = useRouter(),
view = () => {
const routeUrl = router.resolve({
path: '/fileView',
query: { id: props.fileId }
})
window.open(routeUrl.href, '_blank')
}
</script>
<template>
<div v-if="fileName&&fileId" class="accessory text-primary flex items-center flex-wrap">
<span class="cursor-pointer" @click="view">{{ fileName||'-' }}</span>
<p v-if="isDownLoad" class="ml-16 flex items-center text-primary btn cursor-pointer" @click="downloadFile">
<svg-icon name="down-icon" class="mr-2 text-14" />
<span>下载</span>
</p>
</div>
<span v-else>-</span>
</template>
<style lang="less">
.accessory {
.btn {
line-height: 22px;
height: 22px;
background: rgba(9, 103, 253, 0.06);
border-radius: 4px;
border: 1px solid #0967FD;
padding: 0 6px;
font-size: 14px;
width: 60px;
}
}
</style>

+ 64
- 0
src/components/breadcrumb/index.vue Прегледај датотеку

@@ -0,0 +1,64 @@
<script setup name='breadcrumb'>
import { watch, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
// import store from '@/store'
const route = useRoute(),
router = useRouter(),
routeName = ref(),
breadcrumbs = ref([]),
goBack = () => {
router.go(-1)
}
watch(
route,
(val) => {
routeName.value = route.meta.title
breadcrumbs.value = route.matched.filter((item, index) => index !== 0)
},
{ immediate: true, deep: true }
)

</script>

<template>
<div class="ml-20 mt-10">
<div class="flex items-center">
<div
class="flex items-center mr-5 backsize"
@click="goBack"
>
<el-icon>
<ArrowLeft />
</el-icon>返回
</div>
<el-breadcrumb separator="/">
<!-- <el-breadcrumb-item-->
<!-- v-for="tag in store.appStore.tagNavList"-->
<!-- :key="tag.path"-->
<!-- :to="{ name: tag.path, query:tag.query || {}, params: tag.params || {}}"-->
<!-- >{{ tag.name }}</el-breadcrumb-item>-->
<el-breadcrumb-item v-for="item in breadcrumbs" :key="item.name">
{{ item.meta.title }}
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="mt-8 font-semibold routeNameSize"><span>{{ routeName }}</span><img class="rightTopLogo" src="../../assets/images/rightTopLogo.png" alt="" /></div>
</div>
</template>

<style lang='less' scoped>
.backsize{
cursor: pointer;
font-size: 14px;
}
.routeNameSize {
overflow: hidden;
font-size: 20px;
.rightTopLogo{
position: absolute;
top: 0;
right: 0;
}
}

</style>

+ 108
- 0
src/components/elAside/index.vue Прегледај датотеку

@@ -0,0 +1,108 @@
<script setup name="elAside">
import { ref, watch } from 'vue'
import sidebar from '@/components/sidebar/index.vue'
import { useRoute } from 'vue-router'
import store from '@/store'
import { storeToRefs } from 'pinia'
const route = useRoute(),
isCollapse = ref(false),
active = ref(route.path.split('/')[route.path.split('/').length - 1]),
{ menuArr } = storeToRefs(store.menuStore),
emits = defineEmits(['collapseFn']),
clickFn = () => {
isCollapse.value = !isCollapse.value
emits('collapseFn', isCollapse.value)
},
sidebarMenu = ref([])
watch(
route,
(val) => {
sidebarMenu.value =
(menuArr.value.find((i) => i.name === val.meta.topMenu) &&
menuArr.value.find((i) => i.name === val.meta.topMenu).children) ||
[]
},
{ immediate: true, deep: true }
)
</script>

<template>
<div class="mainBox">
<!-- <el-aside class="aside" :width="isCollapse ? '64px' : '200px'"> -->
<el-scrollbar>
<sidebar
:sidebar-menu="sidebarMenu"
:is-collapse="isCollapse"
:active="active"
/>
<div class="collapse">
<el-icon color="rgba(0, 0, 0, 0.45)">
<expand
v-if="isCollapse"
@click="clickFn"
/>
<fold
v-else
@click="clickFn"
/>
</el-icon>
</div>
</el-scrollbar>
<!-- </el-aside> -->
</div>
</template>

<style lang="less" scoped>
.mainBox {
height: 570px;
margin: 8px 0 0 16px;
border-radius: 4px;
position: relative;
.el-scrollbar__bar {
&.is-horizontal {
display: none;
}
}
:deep(.el-menu) {
border: none;
background-color: none;
.el-sub-menu__title {
color: #666;
}
.el-sub-menu{
&.is-active{
.el-sub-menu__title{
color:var(--el-menu-active-color);
}
}
}
.el-menu-item {
color: #666;
border-right: 2px solid transparent;
&.is-active {
color: var(--el-menu-active-color);
border-right: 2px solid var(--el-menu-active-color);
background-color: rgba(27, 113, 227, 0.15);
}
}
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px;
height: 570px;
overflow-y: auto;
}
.collapse {
position: absolute;
bottom: 0;
display: flex;
align-items: center;
height: 40px;
width: 100%;
padding: 0 18px;
border-top: 1px solid rgba(0, 0, 0, 0.09);
}
.main {
padding: 16px;
}
}
</style>

+ 92
- 0
src/components/elTree/index.vue Прегледај датотеку

@@ -0,0 +1,92 @@
<script setup name="roleManage">
import { nextTick, onMounted, ref } from 'vue'
import { districtList } from '@/http/apis/commonApi'
import { storeToRefs } from 'pinia'
import store from '@/store'
const userInfo = storeToRefs(store.userStore).userInfo
const props = defineProps({
defaultExpandAll: {
type: Boolean,
default: true
},
defaultData: {
type: Object,
default: undefined
},
params: {
type: Object,
default: () => {
return {
regionCode: 330500,
regionLevel: 2
}
}
}
})
const treeData = ref([]),
emits = defineEmits(['getTree']),
customNodeClass = {
label: 'name'
},
nodeClick = (data) => {
console.log(data)
emits('getTree', { ...data })
},
treeRef = ref()
onMounted(async () => {
const res = await districtList(props.params)
treeData.value = [res.data]
// 默认选中某个节点
if (props.defaultData) {
nextTick(() => {
treeRef.value && treeRef.value.setCurrentKey(props.defaultData.regionCode, true)
emits('getTree', { ...treeRef.value.getCurrentNode() })
})
} else {
nextTick(() => {
treeRef.value && treeRef.value.setCurrentKey(userInfo.value.regionCode, true)
emits('getTree', { ...treeRef.value.getCurrentNode() })
})
}
})
</script>

<template>
<div class="userManageMenu">
<div class="treeTitle flexItem">
<svg-icon name="location" svg-class="svgIcon" />&nbsp;<span
style="font-weight: 600"
>区划选择</span>
</div>
<el-tree
ref="treeRef"
class="tree"
:data="treeData"
node-key="regionCode"
:highlight-current="true"
:props="customNodeClass"
:default-expand-all="defaultExpandAll"
:current-node-key="defaultData&&defaultData.code||undefined"
:expand-on-click-node="false"
@node-click="nodeClick"
/>
</div>
</template>
<style lang="less" scoped>
.userManageMenu {
background: #fff;
padding: 16px 0px 0px;
box-shadow: 0px 3px 6px 0px rgba(62,99,170,0.06);
height:auto;
:deep(.el-menu){
border-right: none;
}
.tree {
padding: 16px 16px;
}
.treeTitle {
padding: 0px 16px 16px;
border-bottom: 1px solid #d8dadf;
}
}
</style>

+ 27
- 0
src/components/fileView/index.vue Прегледај датотеку

@@ -0,0 +1,27 @@
<script setup name="fileView">
// import { renderAsync } from 'docx-preview'
import { downloadToPdfStreamFileUrl, downloadFileUrl } from '@/utils/uploadAction.js'
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { downFileById } from '@/http/apis/commonApi'
const route = useRoute(),
fileRef = ref()
onMounted(async () => {
if (import.meta.env.MODE === 'production') {
window.location.href = await downloadToPdfStreamFileUrl(route.query.id)
} else {
const res = await downFileById({ fileId: route.query.id })
if (
res.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' || res.type === 'application/msword'
) {
window.location.href = await downloadToPdfStreamFileUrl(route.query.id)
} else {
window.location.href = await downloadFileUrl(route.query.id)
}
}
})
</script>

<template>
<div ref="fileRef"></div>
</template>

+ 267
- 0
src/components/indexTree/index.vue Прегледај датотеку

@@ -0,0 +1,267 @@
<script setup name="indexTree">
import { nextTick, ref, watch } from 'vue'
import { tagFuzzyMatch } from '@/http/apis/performanceEvaluation/indicatorTemplate'

const props = defineProps({
visible: {
type: Boolean,
default: false
},
showCheckbox: {
type: Boolean,
default: false
}, // 是否多选
defaultData: Array, // 用于回显数据,
// 获取数据的参数
params: {
type: Object,
default: () => {}
},
title: {
type: String,
default: '一级指标'
},
// 展开节点
defaultExpandedkeys: {
type: Array,
default: () => {
return []
}
},
// 数据结构
defaultProps: {
type: Object,
default: () => {
return {
children: 'projectIndexes',
label: 'name',
value: 'indexId',
isLeaf: 'isLeaf'
}
}
}
}),
emits = defineEmits(['close', 'getSelectData']), // close 关闭 getSelectIndex:获取数据
handleClose = () => {
emits('close')
inputText.value = ''
},
submit = () => {
emits('getSelectData', selectData.value)
handleClose()
}
const filterText = ref('')
const inputText = ref('')
const treeRef = ref()
watch(
() => props.visible,
val => {
if (val) {
nextTick(async () => {
if (props.defaultData && props.defaultData.length) {
selectData.value = [...props.defaultData]
} else {
filterText.value = undefined
selectData.value = []
}
await getTreeData()
})
}
}
)

// 搜索
// watch(filterText, async val => {
// await getTreeData()
// nextTick(() => {
// treeRef.value && treeRef.value.filter(val)
// })
// })
const search = async () => {
await getTreeData(filterText.value)
nextTick(() => {
treeRef.value && treeRef.value.filter(filterText.value)
})
}
const expandAll = ref(false),
filterNode = (value, data) => {
if (!value) return true
return true
}
// 获取选择的单位
const selectData = ref([]),

checkChange = (data, { checkedKeys, checkedNodes }) => {
if (props.showCheckbox) {
if (checkedKeys.includes(data[props.defaultProps.value])) {
selectData.value.push(data)
} else {
selectData.value = selectData.value.filter(i => i[props.defaultProps.value] !== data[props.defaultProps.value])
}
} else {
selectData.value = [data]
}
},
delData = (index, key) => {
selectData.value.splice(index, 1)
nextTick(() => {
if (props.showCheckbox) {
treeRef.value && treeRef.value.setChecked(key, false)
} else {
treeRef.value && treeRef.value.setCurrentKey(null)
}
})
},
treeData = ref(),
getTreeData = async (filterText) => {
treeData.value = []
const res = await tagFuzzyMatch({ ...props.params, name: filterText || undefined })
const nodeData = changeData(res.data.filter(i => i.projectIndexes?.length))
treeData.value = nodeData
nextTick(() => {
if (props.showCheckbox) {
if (selectData.value && selectData.value.length) {
treeRef.value && treeRef.value.setCheckedNodes(selectData.value)
} else {
treeRef.value && treeRef.value.setCheckedKeys([])
}
} else {
if (selectData.value && selectData.value.length) {
treeRef.value && treeRef.value.setCurrentKey(selectData.value[0].indexId)
} else {
treeRef.value && treeRef.value.setCurrentKey(null)
}
}
})
},
changeData = (data) => {
const arr = []
data.forEach(i => {
arr.push({
...i,
name: i.name || i.indexName,
disabled: !i.indexLevel,
indexId: i.id,
projectIndexes: i.projectIndexes?.length && changeData(i.projectIndexes) || undefined
})
})
return arr
}
</script>
<template>
<el-dialog
:model-value="props.visible"
:title="`选择${title}`"
width="80%"
destroy-on-close
:before-close="handleClose"
>
<div class="unitBox">
<div class="left">
<div class="title">列表</div>
<div class="content">
<el-input
v-model="filterText"
class="mb-16"
placeholder="按名称搜索"
>
<template #append>
<el-button icon="Search" @click="search" />
</template>
</el-input>
<el-scrollbar>
<el-tree
ref="treeRef"
:data="treeData"
class="filter-tree"
:node-key="defaultProps.value"
:default-checked-keys="
(props.defaultData &&
props.defaultData.length &&
props.defaultData
.map(item => item[defaultProps.value])) ||
[]
"
:current-node-key="
(props.defaultData &&
props.defaultData.length &&
props.defaultData[0][defaultProps.value]) ||
undefined
"
:expand-on-click-node="true"
:props="defaultProps"
:show-checkbox="showCheckbox"
:check-on-click-node="!showCheckbox"
check-strictly
:highlight-current="!showCheckbox"
:filter-node-method="filterNode"
:default-expand-all="expandAll"
:default-expanded-keys="defaultExpandedkeys"
@check="checkChange"
/>
</el-scrollbar>
</div>
</div>
<div class="right">
<div class="title">已选{{ title }}</div>
<div class="content">
<el-tag
v-for="(item, index) in selectData"
:key="item.key"
closable
@close="delData(index, item.id, item.type)"
>
{{ item.name }}
</el-tag>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="submit">提交</el-button>
<el-button @click="handleClose">关闭</el-button>
</span>
</template>
</el-dialog>
</template>

<style lang="less">
.unitBox {
display: flex;
height: 500px;
overflow: hidden;
.el-tag {
margin: 0 4px 8px;
}
.title {
padding: 12px;
border-bottom: 1px solid #ccc;
margin-bottom: 12px;
}
.left,
.right {
flex: 1;
border-bottom: 1px solid #ccc;
display: flex;
flex-flow: column;
overflow: hidden;
.content {
flex: 1;
padding: 0 12px;
overflow: scroll;
}
}
.left {
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
.inputText {
padding: 12px;
}
}
.right {
border-top: 1px solid #ccc;
border-right: 1px solid #ccc;
}
}
</style>

+ 9
- 0
src/components/inputRangeNum/index.vue Прегледај датотеку

@@ -0,0 +1,9 @@
<script setup>

</script>
<template>
<div>11</div>
</template>
<style lang='less' scoped>

</style>

+ 337
- 0
src/components/layout/index.vue Прегледај датотеку

@@ -0,0 +1,337 @@
<script setup name="layout">
import { getCurrentInstance, onMounted, ref, nextTick } from 'vue'
import { infoReceived } from '@/http/apis/home'
import store from '@/store'
import { storeToRefs } from 'pinia'
import { useRouter, useRoute, onBeforeRouteUpdate } from 'vue-router'
import Cookie from 'js-cookie'
import { logout } from '@/http/apis/auth'
import moment from 'moment'
const { proxy } = getCurrentInstance(),
{ projectCount, infoList } = storeToRefs(store.countStore) || 0,
userInfo = storeToRefs(store.userStore).userInfo || {},
router = useRouter(),
route = useRoute(),
{ menuArr } = storeToRefs(store.menuStore),
sidebarMenu = ref([]),
toRoute = name => {
sidebarMenu.value = menuArr.value.find(i => i.name === name).children
if (name === 'cockpits') {
const url = router.resolve({ path: '/cockpit' })
window.open(url.href, '_blank')
} else { router.push({ name }) }
},
// 退出登录
loginOut = async () => {
await logout()
Cookie.remove('token')
localStorage.clear()
router.push('/login')
window.location.reload()
},
// 申请权限
apply = () => {
proxy.$messageBox.alert('请联系管理员申请权限(13588274036)', '申请权限', {
showConfirmButton: false
})
},
toNewsDetail = async ({ id, type, instanceId, projectId, meetingId }) => {
if (type === 'PROJECT_REVIEW') {
await infoReceived({ id })
await store.countStore.setCountStore()
router.push({ name: 'handleDuringExamine', query: { instanceId, projectId }})
} else if (type === 'PROJECT_REVIEW_PASS' || type === 'PROJECT_REVIEW_REJECT') {
await infoReceived({ id })
await store.countStore.setCountStore()
router.push({ name: 'projectStore' })
} else if (type === 'PROJECT_REVIEW_BACK') {
await infoReceived({ id })
await store.countStore.setCountStore()
router.push({ name: 'handleAfterGiveBack', query: { instanceId, id: projectId }})
} else if (type === 'EXPERT_REVIEW') {
router.push({ name: 'expertReview' })
} else if (type === 'REVIEW_MEETING') {
router.push({ name: 'meetingDetail', query: { id: meetingId }})
}
},
// 菜单栏判断
currentMenu = ref(),
topMenuRef = ref(),
hidMenuActive = ref(false),
hidMenus = ref(),
hidMenuRef = ref(),
hidMenuHeight = ref(),
hidNum = ref(),
widthDifference = ref(0),
// 判断隐藏菜单
setHidMenu = async () => {
currentMenu.value = JSON.parse(JSON.stringify(menuArr.value.filter((i) => !i.meta.hidden && i.meta.menuType === 'MENU')))
currentMenu.value.forEach((i) => {
i.meta.hidden = false
})
await nextTick()
widthDifference.value = (currentMenu.value.length * 120) - topMenuRef.value.offsetWidth
hidNum.value = Math.floor((((currentMenu.value.length * 120) - topMenuRef.value.offsetWidth) / 120) + 2)
hidMenus.value = currentMenu.value.slice(-hidNum.value)
for (let i = 1; i <= hidNum.value; i++) {
currentMenu.value[currentMenu.value.length - i].meta.hidden = true
}
await nextTick()
hidMenuHeight.value = hidMenuRef.value?.offsetHeight
},
moreMenuClick = () => {
hidMenuActive.value = !hidMenuActive.value
},
openUrl = (url) => {
window.open(url, '_blank')
},
mode = import.meta.env.MODE
onBeforeRouteUpdate(() => {
setHidMenu()
})
onMounted(() => {
setHidMenu()
window.addEventListener('resize', () => {
setHidMenu()
})
document.addEventListener('click', () => {
hidMenuActive.value = false
})
store.countStore.setCountStore()
})
</script>
<template>
<el-container class="h-screen overflow-hidden">
<el-header class="header flex items-center justify-between">
<div class=" flex items-center flex-1">
<p class="title text-black flex-shrink-0">湖州市信息化项目管理系统</p>
<ul ref="topMenuRef" class="flex -mx-20 header-menu flex-1">
<template
v-for="item in currentMenu"
:key="item.name"
>
<li
v-if="!item.meta.hidden"
class="px-12 text-16 cursor-pointer flex items-center"
@click="route.meta.topMenu !== item.name ? toRoute(item.name) : null"
>
<span
class="flex items-center"
:class="[
route.meta.topMenu === item.name
? 'active-menu'
: 'border-transparent'
]"
>
{{ item.meta.title }}
</span>
</li>
</template>
<li
v-if="hidNum>0"
class="px-20 text-16 cursor-pointer whitespace-nowrap relative flex items-center"
@click.stop="moreMenuClick"
>
<span
class="flex items-center"
:class="[
hidMenus.some((i)=>i.name === route.meta.topMenu)
? 'active-menu'
: 'border-transparent'
]"
>
<el-icon class="mr-8" :size="16"><More /></el-icon>
更多菜单
</span>
<div
class="absolute hidMenus w-full top-full z-10 text-center overflow-hidden"
:style="{height:hidMenuActive?`${hidMenuHeight}px`:0,opacity:hidMenuActive?`100%`:0}"
>
<div ref="hidMenuRef" class="py-10">
<p
v-for="i in hidMenus"
:key="i.name"
class="px-20 hidMenu"
@click="toRoute(i.name)"
>
<span
class="flex items-center"
:class="[
route.meta.topMenu === i.name
? 'active-menu'
: 'border-transparent'
]"
>
{{ i?.meta?.title }}
</span>
</p>
</div>
</div>
</li>
</ul>
</div>
<div class="flex items-center flex-shrink-0">
<span class="text-14 cursor-pointer mr-8 pointTit" @click="openUrl(`https://xmglhejia.dsj.lishui.gov.cn/sys/thirdLogin/zzd/login?employeeCode=${userInfo.employeeCode}&employeeName==${userInfo.realName}`)">核价组件</span>
<span class="text-14 cursor-pointer mr-8 pointTit" @click="openUrl(mode==='production'?`https://xmglchacho.dsj.lishui.gov.cn:4430/login?code=${userInfo.employeeCode}`:`http://xmcc.ningdatech.com/login?code=${userInfo.employeeCode}`)">查重组件</span>
<el-tooltip content="操作手册" placement="bottom" effect="light">
<svg-icon name="czsc" class="cursor-pointer text-16" />
</el-tooltip>
<el-badge
:value="projectCount"
:hidden="!projectCount"
class="h-16 ml-12 mr-28"
>
<el-dropdown trigger="hover">
<span class="el-dropdown-link">
<svg-icon name="bell" class="cursor-pointer text-16" />
</span>
<template #dropdown>
<el-dropdown-menu style="width:305px" class="newsDropDownMenu">
<p class="text-center font-semibold border-b border-gray-300 text-14 py-9">消息通知</p>
<el-dropdown-item
v-for="item in infoList?.sort((a, b) => {
return moment(b.createTime).valueOf() - moment(a.createTime).valueOf()
}).filter((item, index) => index < 5)"
:key="item.id"
@click="toNewsDetail(item)"
>
<div class="w-11/12 items-center text-14"><div class="truncate" style="color: rgba(0, 0, 0, 0.65)">{{ item.content }}</div><div style="color: rgba(0, 0, 0, 0.25)">{{ item.createTime }}</div></div>
</el-dropdown-item>
<el-dropdown-item class="justify-center font-semibold border-t border-gray-300" style="color:rgba(0, 87, 255, 1)" @click="router.push({name:'infoCenter'})">查看更多<svg-icon name="forward" svg-class="message-icon mr-8" /></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-badge>
<el-popover
placement="bottom"
:width="200"
trigger="click"
popper-class="loginOutPop"
>
<template #reference>
<a class="flex items-center text-[rgba(0,0,0,0.85)] text-14">
{{ userInfo.realName }}
<el-icon class="el-icon--right"><arrow-down /></el-icon>
</a>
</template>
<div>
<div class="flex p-8">
<span class="flex-shrink-0">当前角色:</span>
<div>
<p v-for="(item,index) in userInfo.userRoleInfoList" :key="index">{{ item.name }}</p>
</div>
</div>
<div class="flex p-8">
<span class="flex-shrink-0">当前区域:</span>
<div>
{{ userInfo.regionName }}
</div>
</div>
<div class="flex p-8">
<span class="flex-shrink-0">当前单位:</span>
<div>
{{ userInfo.empPosUnitName }}
</div>
</div>
<el-divider style="margin: 8px 0 0 0" />
<p v-if="userInfo.regionCode==='330000'&&userInfo.userRoleInfoList?.length<2&&userInfo.userRoleInfoList?.[0].code==='VISITOR'" class="cursor-pointer p-8 login-out" @click="apply"><el-icon><SwitchButton /></el-icon>申请权限</p>
<p class="cursor-pointer p-8 login-out flex items-center" @click="loginOut">
<el-icon class="mr-4"><SwitchButton /></el-icon>
<span>退出登录</span>
</p>
</div>
</el-popover>
</div>
</el-header>
<router-view v-slot="{ Component }">
<component :is="Component" />
</router-view>
</el-container>
</template>

<style lang="less" scoped>
.header {
&.el-header {
height: auto;
background: linear-gradient(180deg, rgba(0, 127, 237, 0.40) 12.79%, rgba(0, 127, 237, 0.13) 64.55%, rgba(0, 127, 237, 0.00) 100%), #FFF;
padding: 25px 24px 47px;
border: 1px solid #CBE4FA;
.title{
color: rgba(0, 0, 0, 0.85);
font-family: PangMenZhengDao;
font-size: 24px;
font-style: normal;
font-weight: 400;
line-height: 24px;
margin-right: 24px;
}

.message-icon {
width: 16px;
height: 16px;
}
.title-icon {
width: 300px;
height: 32px;
}
.right {
height: 32px;
background-color: rgba(27, 113, 227, 0.4);
}
}
.header-menu {
color: rgba(0, 0, 0, 0.85);;
font-weight: 500;
padding-top: 4px;
li:hover {
//background: linear-gradient(90deg, #20DEEF 0%, #007FED 100%);
color:#007FED;
padding: 2px 12px;
//background-color: rgba(0, 0, 0, 0.1);
}
.hidMenus {
left: 0;
border-radius: 4px;
margin-top: 2px;
color:rgba(0, 0, 0, 0.85);
background: linear-gradient(180deg, rgba(0, 127, 237, 0.4) 12.79%, rgba(0, 127, 237, 0.13) 64.55%, rgba(0, 127, 237, 0) 100%), #FFF;
border: 1px solid #CBE4FA;
//background: linear-gradient(90deg, #20DEEF 0%, #007FED 100%);
//background: linear-gradient(270deg, #1b71e3 0%, #27b0e9 100%);
transition: all 0.2s;
z-index: 999;
width: auto;
&.h-0 {
height: 0;
}

&.h-100 {
height: 200px;
}

.hidMenu {
padding-top: 4px;
padding-bottom: 4px;
&:hover {
color:#007FED;
}
}
}
}
#infoDropdown {
width: 305px;
}
.active-menu{
border-radius: 13px;
background: linear-gradient(90deg, #20DEEF 0%, #007FED 100%);
color:#ffffff;
padding: 2px 12px;
}
}
.newsDropDownMenu{
.el-dropdown-menu__item:not(.is-disabled):focus{
background: rgba(0, 87, 255, 0.08);
}
}

</style>

+ 32
- 0
src/components/noData/index.vue Прегледај датотеку

@@ -0,0 +1,32 @@
<script setup name="noData">
</script>

<template>
<div class="mainBox">
<div class="noDataSvg">
<svg-icon name="noData" svg-class="noDataClass" />
</div>
<div>暂无数据</div>
</div>
</template>

<style scoped lang="less">
div {
color: #000000;
opacity: 0.45;
text-align: center;
font-size: 14px;
}
.noDataClass {
width: 50px;
height: 50px;
}
.mainBox {
padding: 40px 0px;
}
.noDataSvg {
display: flex;
justify-content: center;
}
</style>


+ 324
- 0
src/components/orgTree/index.vue Прегледај датотеку

@@ -0,0 +1,324 @@
<script setup name="orgTree">
import { getOrgTree } from '@/http/apis/systemManage/unitManage'
import { getOrgLine } from '@/http/apis/commonApi'
import { nextTick, ref, watch } from 'vue'

const props = defineProps({
visible: {
type: Boolean,
default: false
},
showCheckbox: {
type: Boolean,
default: false
}, // 是否多选
defaultData: Array, // 用于回显数据,
// 获取数据的参数
params: {
type: Object,
default: () => {}
},
// 是否选择到人
isPerson: {
type: Boolean,
default: false
},
title: {
type: String,
default: '单位'
},
// 展开节点
defaultExpandedkeys: {
type: Array,
default: () => {
return []
}
},
// 数据结构
defaultProps: {
type: Object,
default: () => {
return {
children: 'children',
label: 'title',
value: 'key',
isLeaf: 'isLeaf'
}
}
},
type: {
type: String,
default: 'UNIT' // LINE:条线 UNIT:单位
}

}),
emits = defineEmits(['close', 'getSelectUnit']), // close 关闭 getSelectUnit:获取数据
handleClose = () => {
emits('close')
inputText.value = ''
},
submit = () => {
emits('getSelectUnit', selectData.value)
handleClose()
}
const filterText = ref('')
const inputText = ref('')
const treeRef = ref()
const loadNode = async (node, resolve) => {
if (node.level) {
let res
if (props.type === 'LINE') {
res = await getOrgLine({
...props.params,
parentCode: node.data.businessStripCode
})
if (res.data) {
let nodeData
if (props.isPerson && props.showCheckbox) {
nodeData = res.data.map(i => ({ ...i, disabled: i.type === 'ORGANIZATION', isLeaf: i.type === 'MEMBER' }))
} else {
nodeData = res.data.map(i => ({ ...i, isLeaf: i.type === 'MEMBER' }))
}
resolve(nodeData)
} else {
resolve([])
}
} else {
let nodeData
if (node?.data?.children?.length) {
if (props.isPerson && props.showCheckbox) {
nodeData = node?.data?.children.map(i => ({ ...i, disabled: i.type === 'ORGANIZATION', isLeaf: i.type === 'MEMBER' }))
} else {
nodeData = node?.data?.children.map(i => ({ ...i, isLeaf: i.type === 'MEMBER' }))
}
resolve(nodeData)
} else {
res = await getOrgTree({
...props.params,
organizationCode: node.data.key
})

if (res.data?.[0]?.children) {
if (props.isPerson && props.showCheckbox) {
nodeData = res.data?.[0]?.children.map(i => ({ ...i, disabled: i.type === 'ORGANIZATION', isLeaf: i.type === 'MEMBER' }))
} else {
nodeData = res.data?.[0]?.children.map(i => ({ ...i, isLeaf: i.type === 'MEMBER' }))
}
resolve(nodeData)
} else {
resolve([])
}
}
}
}
nextTick(() => {
if (props.showCheckbox) {
if (selectData.value && selectData.value.length) {
treeRef.value && treeRef.value.setCheckedNodes(selectData.value)
} else {
treeRef.value && treeRef.value.setCheckedKeys([])
}
} else {
if (selectData.value && selectData.value.length) {
treeRef.value && treeRef.value.setCurrentKey(selectData.value[0].key)
} else {
treeRef.value && treeRef.value.setCurrentKey(null)
}
}
})
}
watch(
() => props.visible,
val => {
if (val) {
nextTick(async () => {
await getTreeData()
if (props.defaultData && props.defaultData.length) {
selectData.value = [...props.defaultData]
} else {
filterText.value = undefined
selectData.value = []
}
})
}
}
)

// 搜索
// watch(filterText, async val => {
// await getTreeData()
// nextTick(() => {
// treeRef.value && treeRef.value.filter(val)
// })
// })
const search = async () => {
await getTreeData(filterText.value)
nextTick(() => {
treeRef.value && treeRef.value.filter(filterText.value)
})
}
const expandAll = ref(false),
filterNode = (value, data) => {
if (!value) return true
return true
}
// 获取选择的单位
const selectData = ref([]),

checkChange = (data, { checkedKeys, checkedNodes }) => {
if (props.showCheckbox) {
if (checkedKeys.includes(data[props.defaultProps.value])) {
selectData.value.push(data)
} else {
selectData.value = selectData.value.filter(i => i[props.defaultProps.value] !== data[props.defaultProps.value])
}
} else {
selectData.value = [data]
}
},
delData = (index, key, type) => {
selectData.value.splice(index, 1)
nextTick(() => {
if (props.showCheckbox) {
treeRef.value && treeRef.value.setChecked(key, false)
} else {
treeRef.value && treeRef.value.setCurrentKey(null)
}
})
},
treeData = ref(),
getTreeData = async (filterText) => {
treeData.value = []
let res
if (props.type === 'LINE') {
res = await getOrgLine({})
} else {
res = await getOrgTree({ ...props.params, organizationName: !props.isPerson && filterText || undefined, employeeName: props.isPerson && filterText || undefined, parentCode: undefined })
}
let nodeData
if (props.isPerson && props.showCheckbox) {
nodeData = res.data.map(i => ({ ...i, disabled: true }))
} else {
nodeData = res.data.map(i => ({ ...i }))
}
treeData.value = nodeData
}
</script>
<template>
<el-dialog
:model-value="props.visible"
:title="`选择${title}`"
:width="800"
destroy-on-close
:before-close="handleClose"
>
<div class="unitBox">
<div class="left">
<div class="title">列表</div>
<div class="content">
<el-input
v-model="filterText"
class="mb-16"
placeholder="按名称搜索"
>
<template #append>
<el-button icon="Search" @click="search" />
</template>
</el-input>
<el-scrollbar>
<el-tree
ref="treeRef"
:data="treeData"
class="filter-tree"
:node-key="defaultProps.value"
:default-checked-keys="
(props.defaultData &&
props.defaultData.length &&
props.defaultData
.map(item => item[defaultProps.value])) ||
[]
"
:current-node-key="
(props.defaultData &&
props.defaultData.length &&
props.defaultData[0][defaultProps.value]) ||
undefined
"
:load="loadNode"
lazy
:props="defaultProps"
:show-checkbox="showCheckbox"
:check-on-click-node="!showCheckbox"
check-strictly
:highlight-current="!showCheckbox"
:filter-node-method="filterNode"
:default-expand-all="expandAll"
:default-expanded-keys="defaultExpandedkeys"
@check="checkChange"
/>
</el-scrollbar>
</div>
</div>
<div class="right">
<div class="title">已选{{ title }}</div>
<div class="content">
<el-tag
v-for="(item, index) in selectData"
:key="item.key"
closable
@close="delData(index, item.key, item.type)"
>
{{ type === 'LINE' ? item.businessStripName:item.title }}
</el-tag>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="submit">提交</el-button>
<el-button @click="handleClose">关闭</el-button>
</span>
</template>
</el-dialog>
</template>

<style lang="less">
.unitBox {
display: flex;
height: 500px;
overflow: hidden;
.el-tag {
margin: 0 4px 8px;
}
.title {
padding: 12px;
border-bottom: 1px solid #ccc;
margin-bottom: 12px;
}
.left,
.right {
flex: 1;
border-bottom: 1px solid #ccc;
display: flex;
flex-flow: column;
overflow: hidden;
.content {
flex: 1;
padding: 0 12px;
overflow: scroll;
}
}
.left {
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
.inputText {
padding: 12px;
}
}
.right {
border-top: 1px solid #ccc;
border-right: 1px solid #ccc;
}
}
</style>

+ 43
- 0
src/components/sidebar/index.vue Прегледај датотеку

@@ -0,0 +1,43 @@
<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import sidebarItem from './sidebarItem.vue'
import { storeToRefs } from 'pinia'
import store from '@/store'
const props = defineProps({
isCollapse: {
type: Boolean,
default: false
},
sidebarMenu: {
type: Array,
default: () => []
}
})
const userInfo = storeToRefs(store.userStore).userInfo
const active = computed(() => {
const route = useRoute()
const { meta, name } = route
if (meta.activeMenu) {
return meta.activeMenu
}
return name
})
</script>
<template>
<el-menu
:default-active="active"
:collapse="props.isCollapse"
class="el-menu-vertical-demo"
unique-opened
>
<sidebar-item
v-for="item in userInfo.regionCode!=='331123'?props.sidebarMenu.filter(i=>i.name!=='applicationRegist'):props.sidebarMenu"
:key="item.name"
:index="item.name"
:title="item.meta.title"
:item="item"
:active="active"
/>
</el-menu>
</template>

+ 79
- 0
src/components/sidebar/sidebarItem.vue Прегледај датотеку

@@ -0,0 +1,79 @@
<script setup>
import { useRouter, useRoute } from 'vue-router'
import { storeToRefs } from 'pinia'
import store from '@/store'

const { totalNum } = storeToRefs(store.countStore) || 0,
userInfo = storeToRefs(store.userStore).userInfo || {},
router = useRouter(),
route = useRoute(),
props = defineProps({
index: String,
title: String,
item: Object,
active: String
}),
toRoute = (val) => {
router.push({ name: val.index })
},
customIsShow = (item) => {
if ((item.name === 'operationProjectRecord') && userInfo.value.regionCode === '330500') {
return true
}
return true
}

</script>
<template>
<template
v-if="!(item.children && item.children.length) && !item.meta.hidden&&customIsShow(item)"
>
<el-menu-item :index="props.index" @click="toRoute">
<svg-icon
v-if="item.meta.activeIcon&&item.meta.uIcon"
:name="index == active ? item.meta.activeIcon : item.meta.uIcon"
svg-class="menuIcon"
/>
<template #title>
<el-badge
v-if="item.name==='waitMeToHandle'"
:value="totalNum"
:max="99"
class="myBadage"
:hidden="!totalNum"
>
<span>{{ title }}</span>
</el-badge>
<span v-else>{{ title }}</span>
</template>

</el-menu-item>
</template>
<template v-else>
<el-sub-menu v-if="!item.meta.hidden&&(customIsShow(item))" :index="props.index">
<template #title>
<svg-icon
v-if="item.meta.activeIcon&&item.meta.uIcon"
:name="route.path.includes(index)||route.meta.activeMenu?.includes(index) ? item.meta.activeIcon : item.meta.uIcon"
svg-class="menuIcon"
/>
<span>{{ title }}</span>
</template>
<sidebar-item
v-for="i in item.children"
:key="i.name"
:index="i.name"
:title="i.meta.title"
:item="i"
/>
</el-sub-menu>
</template>
</template>

<style scoped lang='less'>
.menuIcon {
width: 16px;
height: 16px;
margin-right: 4px;
}
</style>

+ 27
- 0
src/components/svgIcon/index.vue Прегледај датотеку

@@ -0,0 +1,27 @@
<script setup name="svgIcon">
import { computed, ref } from 'vue'

const props = defineProps({
prefix: { type: String, default: 'icon' },
name: { type: String, required: true },
svgClass: { type: String, default: '' },
color: { type: String, default: '' }
})
const symbolId = ref(computed(() => `#${props.prefix}-${props.name}`))
</script>

<template>
<svg aria-hidden="true" :class="['svg-icon', svgClass]">
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>

<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.1em;
overflow: hidden;
fill: currentColor;
}
</style>

+ 16
- 0
src/components/tableList/E-expand.js Прегледај датотеку

@@ -0,0 +1,16 @@
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Expand',
props: {
row: Object,
render: Function,
index: Number,
column: {
type: Object,
default: null
}
},
render () {
return this.render(this.row)
}
})

+ 243
- 0
src/components/tableList/index.vue Прегледај датотеку

@@ -0,0 +1,243 @@
<script setup name="tableList">
import Eexpand from './E-expand'
import { ref, reactive, onMounted } from 'vue'
const props = defineProps({
column: {
type: Object
},
data: {
type: Array
},
size: String,
emptyTemp: {
type: Boolean,
default: true
},
pagination: {
type: Boolean,
default: true
},
height: {
type: Number,
default: null
},
rowKey: {
type: [String, Function]
},
selectable: {
type: Function,
default: () => true
},
// 合并单元格
spanMethod: {
type: Function,
default: () => {
return {
rowspan: 1,
colspan: 1
}
}
},
// 是否带斑马纹
stripe: {
type: Boolean,
default: true
},
// 是否有边框
border: {
type: Boolean,
default: false
},
// 分页
pageSizes: {
type: Array,
default: () => [10, 20, 30, 40]
},
total: {
type: Number,
default: 0
},
small: {
type: Boolean,
default: false
}
}),
emits = defineEmits(['selectionChange', 'getTableData', 'handleTable', 'radioChange', 'selectionRow', 'selectionAll']),
tableRef = ref(null),
// 多选
selectionChange = selection => {
emits('selectionChange', selection)
},
selectRow = (selection, row) => {
emits('selectionRow', selection, row)
},
selectAll = (selection) => {
emits('selectionAll', selection)
},
// 单选
radio = ref(),
radioChange = (row) => {
emits('radioChange', row)
},
setRadio = (data) => {
radio.value = data
},
// 分页
pageParams = reactive({
pageNumber: 1,
pageSize: props.pageSizes[0]
}),
handleSizeChange = () => {
emits('getTableData', pageParams)
},
handleCurrentChange = () => {
emits('getTableData', pageParams)
},
// 多选表格-默认选中
toggleRowSelect = ref((row, selected) => {
tableRef.value.toggleRowSelection(row, selected)
}),
getSelectRows = () => {
return tableRef.value.getSelectionRows()
}
onMounted(() => {
const tableDom = document.querySelector('#out-table')
emits('handleTable', tableDom)
})
defineExpose({ tableRef, pageParams, setRadio, toggleRowSelect, getSelectRows })
</script>

<template>
<div>
<el-table
id="out-table"
ref="tableRef"
:data="props.data"
:stripe="props.stripe"
:height="props.height"
:row-key="props.rowKey"
:size="size"
:span-method="spanMethod"
:border="props.border"
@selection-change="selectionChange"
@select="selectRow"
@select-all="selectAll"
>
<template v-if="props.emptyTemp" #empty><el-empty /></template>
<template v-for="item in props.column" :key="item.key">
<template v-if="item.type">
<el-table-column
v-if="item.type === 'index'"
type="index"
:label="item.label"
:width="item.width"
/>
<el-table-column
v-if="item.type === 'selection'"
type="selection"
:width="item.width"
:reserve-selection="item.reserveSelection"
:selectable="props.selectable"
/>
<el-table-column v-if="item.type === 'radio'" type="radio" :width="item.width">
<template #default="scope">
<el-radio
:key="scope.row[item.key]"
v-model="radio"
:label="scope.row[item.key]"
:disabled="scope.row.isRadioDisabled"
@change="radioChange(scope.row)"
>&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column v-if="item.type === 'expand'" type="expand">
<template #default="scope">
<slot name="expand" :scope="scope"></slot>
</template>
</el-table-column>
</template>
<template v-else-if="item.children">
<el-table-column
:label="item.label"
:prop="item.prop"
:width="item.width"
:min-width="item.minWidth"
:show-overflow-tooltip="item.showOverflowTooltip || false"
:fixed="item.fixed || false"
:column-key="item.columnKey"
>
<el-table-column
v-for="(child,key) in item.children"
:key="key"
:label="child.label"
:prop="child.prop"
:width="child.width"
:min-width="child.minWidth"
:show-overflow-tooltip="child.showOverflowTooltip || false"
:fixed="child.fixed || false"
:column-key="child.columnKey"
>
<template #default="scope">
<template v-if="child.slot">
<slot :name="child.slot" :scope="scope"></slot>
</template>
<template v-if="child.render">
<Eexpand
:column="child"
:row="scope.row"
:render="child.render"
:index="scope.$index"
/>
</template>
</template></el-table-column>
</el-table-column>
</template>
<template v-else>
<el-table-column
:label="item.label"
:prop="item.prop"
:width="item.width"
:min-width="item.minWidth"
:show-overflow-tooltip="item.showOverflowTooltip || false"
:fixed="item.fixed || false"
:column-key="item.columnKey"
>
<template #default="scope">
<template v-if="item.slot">
<slot :name="item.slot" :scope="scope"></slot>
</template>
<template v-if="item.render">
<Eexpand
:column="item"
:row="scope.row"
:render="item.render"
:index="scope.$index"
/>
</template>
</template>
</el-table-column>
</template>
</template>
</el-table>
<el-pagination
v-if="pagination"
v-model:currentPage="pageParams.pageNumber"
v-model:page-size="pageParams.pageSize"
:small="small"
background
:page-sizes="props.pageSizes"
layout="total, sizes, prev, pager, next"
:total="props.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>

<style scoped lang="less">
.el-pagination {
padding: 16px 16px 0 16px;
flex-wrap: wrap;
justify-content: flex-end;
}
</style>

+ 62
- 0
src/components/tagsView/index.vue Прегледај датотеку

@@ -0,0 +1,62 @@
<script setup name="tagsView">
import { watch, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import store from '@/store'
const route = useRoute(),
router = useRouter(),
clickFn = ({ props: { name }}) => {
router.push({
name: name,
query: store.appStore.tagNavList.find(i => i.path === name).query || {},
params: store.appStore.tagNavList.find(i => i.path === name).params || {}
})
},
removeTab = (path) => {
store.appStore.removeTag(path)
},
acvtie = ref(route.path.split('/')[route.path.split('/').length - 1])
watch(
route,
(val) => {
acvtie.value = route.path.split('/')[route.path.split('/').length - 1]
},
{ immediate: true, deep: true }
)
</script>

<template>
<div class="scroll-outer">
<div class="scroll-body">
<el-tabs
v-model="acvtie"
type="card"
class="demo-tabs"
:closable="store.appStore.tagNavList.length!=1"
@tab-remove="removeTab"
@tab-click="clickFn"
>
<el-tab-pane
v-for="tag in store.appStore.tagNavList"
:key="tag.path"
:data="JSON.stringify(tag)"
:label="tag.name"
:name="tag.path"
/></el-tabs>
</div>
</div>
</template>

<style scoped lang="less">
.scroll-outer {
padding: 5px 0px 0px 20px;
width: calc(100vw - 200px);
.scroll-body {
overflow: visible;
white-space: nowrap;
:deep(.el-tabs__header){
margin: 0px;
}
}
}
</style>


Loading…
Откажи
Сачувај