Merge branch 'main' of http://118.182.97.76:3000/admin/zhyc-sheep-ui
This commit is contained in:
44
src/api/fileManagement/pedigree.js
Normal file
44
src/api/fileManagement/pedigree.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询VIEW列表
|
||||
export function listPedigree(query) {
|
||||
return request({
|
||||
url: '/system/pedigree/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询VIEW详细
|
||||
export function getPedigree(id) {
|
||||
return request({
|
||||
url: '/system/pedigree/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增VIEW
|
||||
export function addPedigree(data) {
|
||||
return request({
|
||||
url: '/system/pedigree',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改VIEW
|
||||
export function updatePedigree(data) {
|
||||
return request({
|
||||
url: '/system/pedigree',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除VIEW
|
||||
export function delPedigree(id) {
|
||||
return request({
|
||||
url: '/system/pedigree/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
428
src/views/fileManagement/pedigree/index.vue
Normal file
428
src/views/fileManagement/pedigree/index.vue
Normal file
@@ -0,0 +1,428 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 查询+导出区域 -->
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
|
||||
<el-form-item label="管理耳号" prop="bsManageTags">
|
||||
<el-select
|
||||
v-model="queryParams.bsManageTags"
|
||||
placeholder="请选择管理耳号"
|
||||
clearable
|
||||
@change="handleQuery"
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in tagOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['system:pedigree:export']"
|
||||
style="margin-left: 10px"
|
||||
>导出</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 横向系谱图(优化横向布局) -->
|
||||
<div class="pedigree-container" v-loading="loading">
|
||||
<div class="empty-tip" v-if="!pedigreeData.id">
|
||||
请选择管理耳号查询系谱数据
|
||||
</div>
|
||||
|
||||
<div class="pedigree-tree" v-else>
|
||||
<!-- 第一层:核心羊(左侧) -->
|
||||
<div class="pedigree-level level-1">
|
||||
<div class="pedigree-node" @click="handleNodeClick(pedigreeData.bsManageTags)">
|
||||
<div class="sheep-icon core-sheep">🐑</div>
|
||||
<span class="node-text">{{ pedigreeData.bsManageTags }}-{{ getGenderText(pedigreeData.gender) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第一层与第二层连线 -->
|
||||
<div class="pedigree-line line-1-2"></div>
|
||||
|
||||
<!-- 第二层:父/母羊(中间) -->
|
||||
<div class="pedigree-level level-2">
|
||||
<!-- 母本(上) -->
|
||||
<div class="pedigree-node" v-if="pedigreeData.motherData" @click="handleNodeClick(pedigreeData.motherData.bsManageTags || pedigreeData.motherManageTags)">
|
||||
<div class="sheep-icon normal-sheep">🐑</div>
|
||||
<span class="node-text">母亲耳号:{{ pedigreeData.motherData.bsManageTags || pedigreeData.motherManageTags }}</span>
|
||||
</div>
|
||||
<!-- 父本(下) -->
|
||||
<div class="pedigree-node" v-if="pedigreeData.fatherData" @click="handleNodeClick(pedigreeData.fatherData.bsManageTags || pedigreeData.fatherManageTags)">
|
||||
<div class="sheep-icon normal-sheep">🐑</div>
|
||||
<span class="node-text">父亲耳号:{{ pedigreeData.fatherData.bsManageTags || pedigreeData.fatherManageTags }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二层与第三层连线 -->
|
||||
<div class="pedigree-line line-2-3"></div>
|
||||
|
||||
<!-- 第三层:祖父母/外祖父母(右侧) -->
|
||||
<div class="pedigree-level level-3">
|
||||
<!-- 外祖母(上1) -->
|
||||
<div class="pedigree-node" v-if="pedigreeData.maternalGrandmotherData" @click="handleNodeClick(pedigreeData.maternalGrandmotherData.bsManageTags || pedigreeData.maternalGrandmotherManageTags)">
|
||||
<div class="sheep-icon normal-sheep">🐑</div>
|
||||
<span class="node-text">外祖母耳号:{{ pedigreeData.maternalGrandmotherData.bsManageTags || pedigreeData.maternalGrandmotherManageTags }}</span>
|
||||
</div>
|
||||
<!-- 外祖父(上2) -->
|
||||
<div class="pedigree-node" v-if="pedigreeData.maternalGrandfatherData" @click="handleNodeClick(pedigreeData.maternalGrandfatherData.bsManageTags || pedigreeData.maternalGrandfatherManageTags)">
|
||||
<div class="sheep-icon normal-sheep">🐑</div>
|
||||
<span class="node-text">外祖父耳号:{{ pedigreeData.maternalGrandfatherData.bsManageTags || pedigreeData.maternalGrandfatherManageTags }}</span>
|
||||
</div>
|
||||
<!-- 祖母(下1) -->
|
||||
<div class="pedigree-node" v-if="pedigreeData.paternalGrandmotherData" @click="handleNodeClick(pedigreeData.paternalGrandmotherData.bsManageTags || pedigreeData.grandmotherManageTags)">
|
||||
<div class="sheep-icon normal-sheep">🐑</div>
|
||||
<span class="node-text">祖母耳号:{{ pedigreeData.paternalGrandmotherData.bsManageTags || pedigreeData.grandmotherManageTags }}</span>
|
||||
</div>
|
||||
<!-- 祖父(下2) -->
|
||||
<div class="pedigree-node" v-if="pedigreeData.paternalGrandfatherData" @click="handleNodeClick(pedigreeData.paternalGrandfatherData.bsManageTags || pedigreeData.grandfatherManageTags)">
|
||||
<div class="sheep-icon normal-sheep">🐑</div>
|
||||
<span class="node-text">祖父耳号:{{ pedigreeData.paternalGrandfatherData.bsManageTags || pedigreeData.grandfatherManageTags }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Pedigree">
|
||||
import { ref, reactive, toRefs, getCurrentInstance } from 'vue'
|
||||
import { listPedigree, getPedigree } from "@/api/fileManagement/pedigree"
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
// 管理耳号下拉选项
|
||||
const tagOptions = ref([])
|
||||
// 系谱核心数据
|
||||
const pedigreeData = ref({})
|
||||
const loading = ref(false)
|
||||
|
||||
// 查询参数
|
||||
const data = reactive({
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
bsManageTags: null
|
||||
}
|
||||
})
|
||||
const { queryParams } = toRefs(data)
|
||||
|
||||
/** 性别转换:数字转文字 */
|
||||
function getGenderText(gender) {
|
||||
if (gender === 1) return '母'
|
||||
if (gender === 2) return '公'
|
||||
if (gender === 3) return '阉羊'
|
||||
if (gender === 4) return '兼性'
|
||||
|
||||
return '未知'
|
||||
}
|
||||
|
||||
/** 初始化耳号下拉 */
|
||||
function initTagOptions() {
|
||||
listPedigree({ pageSize: 9999 }).then(response => {
|
||||
tagOptions.value = response.rows.map(item => ({
|
||||
label: item.bsManageTags,
|
||||
value: item.bsManageTags
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/** 根据耳号查询单只羊数据 */
|
||||
async function getSheepByTag(tag) {
|
||||
const listRes = await listPedigree({ bsManageTags: tag })
|
||||
return listRes.rows[0] || {}
|
||||
}
|
||||
|
||||
/** 构建横向系谱数据 */
|
||||
async function buildPedigreeData(baseTag) {
|
||||
const baseSheep = await getSheepByTag(baseTag)
|
||||
if (!baseSheep.id) {
|
||||
pedigreeData.value = {}
|
||||
return
|
||||
}
|
||||
|
||||
const pedigree = { ...baseSheep }
|
||||
|
||||
// 获取母本+外祖父母数据
|
||||
if (baseSheep.bsMotherId) {
|
||||
try {
|
||||
const motherRes = await getPedigree(baseSheep.bsMotherId)
|
||||
pedigree.motherData = motherRes.data || {}
|
||||
if (pedigree.motherData.bsFatherId) {
|
||||
const mgfRes = await getPedigree(pedigree.motherData.bsFatherId)
|
||||
pedigree.maternalGrandfatherData = mgfRes.data || {}
|
||||
}
|
||||
if (pedigree.motherData.bsMotherId) {
|
||||
const mgmRes = await getPedigree(pedigree.motherData.bsMotherId)
|
||||
pedigree.maternalGrandmotherData = mgmRes.data || {}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("获取母本系谱失败:", e)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取父本+祖父母数据
|
||||
if (baseSheep.bsFatherId) {
|
||||
try {
|
||||
const fatherRes = await getPedigree(baseSheep.bsFatherId)
|
||||
pedigree.fatherData = fatherRes.data || {}
|
||||
if (pedigree.fatherData.bsFatherId) {
|
||||
const pgfRes = await getPedigree(pedigree.fatherData.bsFatherId)
|
||||
pedigree.paternalGrandfatherData = pgfRes.data || {}
|
||||
}
|
||||
if (pedigree.fatherData.bsMotherId) {
|
||||
const pgfRes = await getPedigree(pedigree.fatherData.bsMotherId)
|
||||
pedigree.paternalGrandmotherData = pgfRes.data || {}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("获取父本系谱失败:", e)
|
||||
}
|
||||
}
|
||||
|
||||
pedigreeData.value = pedigree
|
||||
}
|
||||
|
||||
/** 查询系谱数据 */
|
||||
async function getList() {
|
||||
if (!queryParams.value.bsManageTags) {
|
||||
proxy.$modal.msgWarning("请选择管理耳号后查询")
|
||||
pedigreeData.value = {}
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
await buildPedigreeData(queryParams.value.bsManageTags)
|
||||
} catch (e) {
|
||||
console.error("查询系谱失败:", e)
|
||||
pedigreeData.value = {}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 点击节点切换系谱树 */
|
||||
async function handleNodeClick(tag) {
|
||||
if (!tag) {
|
||||
proxy.$modal.msgWarning("该羊只暂无有效耳号,无法查询系谱")
|
||||
return
|
||||
}
|
||||
// 更新查询参数为点击节点的耳号
|
||||
queryParams.value.bsManageTags = tag
|
||||
// 重新查询并渲染该羊的系谱树
|
||||
await getList()
|
||||
}
|
||||
|
||||
/** 搜索/重置/导出 */
|
||||
function handleQuery() {
|
||||
getList()
|
||||
}
|
||||
function resetQuery() {
|
||||
proxy.resetForm("queryRef")
|
||||
queryParams.value.bsManageTags = null
|
||||
pedigreeData.value = {}
|
||||
}
|
||||
function handleExport() {
|
||||
if (!queryParams.value.bsManageTags) {
|
||||
proxy.$modal.msgWarning("请选择管理耳号后导出")
|
||||
return
|
||||
}
|
||||
proxy.download('system/pedigree/export', {
|
||||
...queryParams.value
|
||||
}, `pedigree_${queryParams.value.bsManageTags}_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
// 初始化
|
||||
initTagOptions()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 系谱容器整体样式 */
|
||||
.pedigree-container {
|
||||
margin-top: 20px;
|
||||
padding: 30px 20px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
min-height: 500px;
|
||||
position: relative;
|
||||
overflow-x: auto; /* 适配小屏幕横向滚动 */
|
||||
}
|
||||
|
||||
/* 空状态提示 */
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
line-height: 500px;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 系谱树核心布局 - 横向排列 */
|
||||
.pedigree-tree {
|
||||
display: flex;
|
||||
flex-direction: row; /* 横向布局核心 */
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
min-width: 1000px; /* 保证横向布局基础宽度 */
|
||||
height: 450px;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 层级通用样式 */
|
||||
.pedigree-level {
|
||||
display: flex;
|
||||
flex-direction: column; /* 每个层级内部垂直排列 */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
flex: 0 0 200px; /* 每个层级固定宽度 */
|
||||
gap: 40px; /* 层级内节点间距 */
|
||||
}
|
||||
|
||||
/* 第一层(核心羊)- 最左侧 */
|
||||
.level-1 {
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
gap: 0; /* 核心羊只有一个节点 */
|
||||
}
|
||||
|
||||
/* 第二层(父母)- 中间 */
|
||||
.level-2 {
|
||||
position: absolute;
|
||||
left: 300px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/* 第三层(祖父母/外祖父母)- 最右侧 */
|
||||
.level-3 {
|
||||
position: absolute;
|
||||
left: 550px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
gap: 20px; /* 四层节点缩小间距 */
|
||||
}
|
||||
|
||||
/* 节点样式优化 */
|
||||
.pedigree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer; /* 鼠标悬浮显示手型,提示可点击 */
|
||||
}
|
||||
|
||||
.pedigree-node:hover {
|
||||
background: #e8f4ff; /* 悬浮浅蓝背景,增强交互提示 */
|
||||
transform: scale(1.05); /* 悬浮放大效果 */
|
||||
}
|
||||
|
||||
/* 羊图标优化 */
|
||||
.sheep-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.15); /* 阴影提升质感 */
|
||||
}
|
||||
|
||||
/* 核心羊图标(蓝色渐变) */
|
||||
.core-sheep {
|
||||
background: linear-gradient(135deg, #409eff, #66b1ff);
|
||||
}
|
||||
|
||||
/* 普通羊图标(灰色渐变) */
|
||||
.normal-sheep {
|
||||
background: linear-gradient(135deg, #e5e6eb, #c9c9c9);
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 节点文字优化 */
|
||||
.node-text {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
/* 连线样式 - 核心羊到父母(横向连线) */
|
||||
.line-1-2 {
|
||||
position: absolute;
|
||||
left: 100px;
|
||||
top: 50%;
|
||||
width: 180px;
|
||||
height: 2px;
|
||||
background: #409eff; /* 主色调连线 */
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/* 核心羊到父母的垂直分支线 */
|
||||
.line-1-2::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 180px;
|
||||
top: -60px;
|
||||
width: 2px;
|
||||
height: 120px;
|
||||
background: #409eff;
|
||||
}
|
||||
|
||||
/* 连线样式 - 父母到祖父母(横向连线) */
|
||||
.line-2-3 {
|
||||
position: absolute;
|
||||
left: 350px;
|
||||
top: 50%;
|
||||
width: 180px;
|
||||
height: 2px;
|
||||
background: #409eff;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/* 父母到祖父母的垂直分支线 */
|
||||
.line-2-3::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 180px;
|
||||
top: -100px;
|
||||
width: 2px;
|
||||
height: 200px;
|
||||
background: #409eff;
|
||||
}
|
||||
|
||||
/* 适配小屏幕 */
|
||||
@media (max-width: 1200px) {
|
||||
.pedigree-container {
|
||||
padding: 20px 10px;
|
||||
}
|
||||
.pedigree-tree {
|
||||
min-width: 800px;
|
||||
}
|
||||
.level-1 { left: 20px; }
|
||||
.level-2 { left: 250px; }
|
||||
.level-3 { left: 480px; }
|
||||
.line-1-2 { width: 210px; }
|
||||
.line-2-3 { width: 210px; left: 300px; }
|
||||
}
|
||||
</style>
|
||||
@@ -1,110 +0,0 @@
|
||||
<!-- src/views/fileManagement/sheep_pedigree/index.vue -->
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 诊断信息 -->
|
||||
<div style="background:#f0f9ff; padding:20px; margin-bottom:20px; border:1px solid #bae0ff;">
|
||||
<h3>页面加载诊断</h3>
|
||||
<p><strong>路由路径:</strong> {{ $route.path }}</p>
|
||||
<p><strong>路由参数:</strong> {{ $route.params }}</p>
|
||||
<p><strong>查询参数:</strong> {{ $route.query }}</p>
|
||||
<p><strong>组件路径:</strong> {{ $route.meta?.component }}</p>
|
||||
<p><strong>菜单配置:</strong> {{ menuConfig }}</p>
|
||||
<el-button @click="reloadPage" type="primary">重新加载</el-button>
|
||||
<el-button @click="goBack" type="info">返回</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 测试内容 -->
|
||||
<div style="text-align:center; padding:50px;">
|
||||
<h1 style="color:#1890ff;">羊只系谱管理页面</h1>
|
||||
<p v-if="isFrameIssue" style="color:red; font-weight:bold;">
|
||||
⚠️ 检测到 isFrame=1 问题!请修改菜单配置为"是否外链=否"
|
||||
</p>
|
||||
<p>这是一个测试页面,如果看到此内容,说明组件加载成功</p>
|
||||
<el-button type="success" @click="testClick">测试按钮</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SheepPedigree",
|
||||
data() {
|
||||
return {
|
||||
menuConfig: {},
|
||||
isFrameIssue: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
console.log("=== 羊只系谱页面创建 ===");
|
||||
console.log("1. 路由信息:", this.$route);
|
||||
console.log("2. 路由匹配:", this.$router.currentRoute);
|
||||
console.log("3. 权限信息:", this.$store.state.user.permissions);
|
||||
|
||||
// 检测是否是isFrame问题
|
||||
const route = this.$route;
|
||||
if (route.meta && route.meta.isFrame === '1') {
|
||||
this.isFrameIssue = true;
|
||||
console.error("检测到isFrame=1,会导致页面显示问题!");
|
||||
}
|
||||
|
||||
// 获取当前路由对应的菜单配置
|
||||
this.getMenuConfig();
|
||||
|
||||
// 测试API
|
||||
this.testApiConnection();
|
||||
},
|
||||
mounted() {
|
||||
console.log("页面已挂载到DOM");
|
||||
console.log("当前URL:", window.location.href);
|
||||
},
|
||||
methods: {
|
||||
getMenuConfig() {
|
||||
// 从Vuex中查找当前路由对应的菜单
|
||||
const routes = this.$store.state.permission.routes || [];
|
||||
this.findMenuConfig(routes, this.$route.path);
|
||||
},
|
||||
|
||||
findMenuConfig(menus, path) {
|
||||
for (const menu of menus) {
|
||||
if (menu.path === path) {
|
||||
this.menuConfig = menu;
|
||||
console.log("找到菜单配置:", menu);
|
||||
return;
|
||||
}
|
||||
if (menu.children) {
|
||||
this.findMenuConfig(menu.children, path);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async testApiConnection() {
|
||||
try {
|
||||
// 测试调用API(使用已有的API)
|
||||
const response = await this.$axios.get('/system/dict/data/type/sheep_type');
|
||||
console.log("API测试成功:", response.data);
|
||||
} catch (error) {
|
||||
console.warn("API测试失败:", error.response || error.message);
|
||||
}
|
||||
},
|
||||
|
||||
testClick() {
|
||||
this.$message.success("按钮点击成功!页面功能正常");
|
||||
},
|
||||
|
||||
reloadPage() {
|
||||
window.location.reload();
|
||||
},
|
||||
|
||||
goBack() {
|
||||
this.$router.back();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
min-height: 500px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user