Commit dfd5cc55 by jiang'yun

修改

parent fa53ed97
<template>
<div>
<el-tooltip content="设置" placement="bottom" effect="light">
<el-button @click="editProperties" :class="{ 'active': showLineStylePanel }">
<i class="el-icon-setting"></i>
</el-button>
</el-tooltip>
<el-tooltip content="上传文件" placement="bottom" effect="light">
<el-button @click="triggerFileUpload">
<i class="el-icon-upload"></i>
</el-button>
</el-tooltip>
<el-dialog title="设置" :visible.sync="showPropertiesDialog" width="100%">
<el-form ref="form" label-width="130px">
<el-row>
<el-col :span="5">
<el-form-item label="颜色集合">
<el-select v-model="colorMapSelect" placeholder="请选择颜色集合" @change="changeColor">
<el-option v-for="item in listNameColorMaps" :label="item" :value="item"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="归一化类型">
<el-select v-model="NormalizationType" @change="changeNor" placeholder="请选择归一化类型">
<el-option v-for="item in NormalizationTypeData" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="归一化比例">
<el-slider @change="changeNor" style="width: 180px;" v-model="NormalizationBl" :min="0.1" :max="5"
:step="0.1" show-stops>
</el-slider>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="每英寸道数">
<el-input v-model="mycds" @blur="changeMycds"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="5">
<el-form-item label="每秒英寸数">
<el-input v-model="msycs" @blur="changeMycds"></el-input>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="采样插值类型">
<el-select v-model="samplesType" @change="changeInt" placeholder="请选择采样插值类型">
<el-option v-for="item in InterpolationType" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="采样插值边缘">
<el-select v-model="samplesEdge" @change="changeInt" placeholder="请选择采样插值边缘">
<el-option v-for="item in InterpolationEdge" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="道插值类型">
<el-select v-model="tracesEdge" @change="changeInt" placeholder="请选择道插值类型">
<el-option v-for="item in InterpolationType" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="5">
<el-form-item label="道插值边缘">
<el-select v-model="tracesType" @change="changeInt" placeholder="请选择道插值边缘">
<el-option v-for="item in InterpolationEdge" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="绘图类型">
<el-checkbox v-model="Wiggle" @change="changePlotType">Wiggle</el-checkbox>
<el-checkbox v-model="Reversed" @change="changePlotType">Reversed</el-checkbox>
<el-checkbox v-model="PositiveFill" @change="changePlotType">Positive fill</el-checkbox>
<el-checkbox v-model="NegativeFill" @change="changePlotType">Negative fill</el-checkbox>
<el-checkbox v-model="PositiveColorFill" @change="changePlotType">Positive color fill</el-checkbox>
<el-checkbox v-model="NegativeColorFill" @change="changePlotType">Negative color fill</el-checkbox>
<el-checkbox v-model="SimpleDensity" @change="changePlotType">Simple density</el-checkbox>
<el-checkbox v-model="InterpolatedDensity" @change="changePlotType">Interpolated density</el-checkbox>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="Wiggle-裁剪因子">
<el-input v-model="ClippingFactor" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="5">
<el-form-item label="Wiggle-抽取间距">
<el-input v-model="DecimationSpacing" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="Wiggle-密度抽取">
<el-checkbox v-model="densityDecimation" @change="changePlotType">Density decimation(密度抽取)</el-checkbox>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="Clipping mode(裁剪模式)">
<el-select v-model="ClippingMode" @change="changePlotType" placeholder="请选择裁剪模式">
<el-option v-for="item in ClippingModeData" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="3">
<el-form-item label="TaperFilter(滤波)">
<el-checkbox v-model="TaperFilterEnbled" @change="changePlotType">启用滤波</el-checkbox>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="f1">
<el-input v-model="f1" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="f2">
<el-input v-model="f2" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="f3">
<el-input v-model="f3" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="f4">
<el-input v-model="f4" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="采样率">
<el-input v-model="sampleRate" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="带通模式">
<el-checkbox v-model="passFlag" @change="changePlotType">带通模式</el-checkbox>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="3">
<el-form-item label="AGC">
<el-checkbox v-model="AGCEnbled" @change="changePlotType">启用AGC</el-checkbox>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="AGC length">
<el-input v-model="AGCLength" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="Desired average">
<el-input v-model="DesiredAverage" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="Noise reduction">
<el-select v-model="NoiseReduction" @change="changePlotType" placeholder="请选择降噪">
<el-option v-for="item in NoiseReductionData" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="Noise reduction percentage">
<el-input v-model="NoiseReductionPercentage" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="Start sample">
<el-input v-model="StartSample" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="Step">
<el-input v-model="Step" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="4">
<el-form-item label="Units">
<el-select v-model="Units" @change="changePlotType" placeholder="请选择单位">
<el-option v-for="item in UnitsData" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="Window length">
<el-input v-model="WindowLength" style="width: 80px" @blur="changePlotType"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="3">
<el-form-item label="Reverse">
<el-checkbox v-model="ReverseEnbled" @change="changePlotType">启用Reverse</el-checkbox>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="inverted">
<el-checkbox v-model="inverted" @change="changePlotType">启用inverted</el-checkbox>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="reversed">
<el-checkbox v-model="reversed" @change="changePlotType">启用reversed</el-checkbox>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="showPropertiesDialog = false">取 消</el-button>
<el-button type="primary" @click="showPropertiesDialog = false">确 定</el-button>
</div>
</el-dialog>
<canvas ref="plot" id="canvas" />
<!-- <PropertiesDialog-->
<!-- :show-dialog="showPropertiesDialog"-->
<!-- :node-props="nodeProps"-->
<!-- :ajv="ajv"-->
<!-- :schema="schema"-->
<!-- @close="applyProps"-->
<!-- />-->
</div>
</template>
<script>
import { SeismicPlot } from './App.js';
import PropertiesDialog from './ui/PropertiesDialog.vue';
import { getAjv, setNodeProps, getNodeProps } from './ui/DialogPropertyUtils';
import { getToken } from "@/utils/auth";
import { SegyReader } from '@int/geotoolkit/seismic/data/SegyReader';
import { LocalFile } from '@int/geotoolkit/seismic/data/LocalFile';
import { Reverse } from '@int/geotoolkit/seismic/pipeline/processor/Reverse';
import { AGC } from '@int/geotoolkit/seismic/pipeline/processor/AGC';
import { TaperFilterProcess } from '@int/geotoolkit/seismic/analysis/filters/TaperFilterProcess';
import { NormalizationType } from '@int/geotoolkit/seismic/pipeline/NormalizationType';
import { SeismicPipeline } from '@int/geotoolkit/seismic/pipeline/SeismicPipeline';
import { SeismicColors } from '@int/geotoolkit/seismic/util/SeismicColors';
let seismicPlot = null;
export default {
name: "index",
components: {
PropertiesDialog
},
data() {
return {
showLineStylePanel: false,
showPropertiesDialog: false,
nodeProps: null,
ajv: null,
schema: null,
chartIsActive: false,
tableIsActive: false,
listNameColorMaps: [
"WhiteBlack",
"RedWhiteBlack",
"RedWhiteBlue",
"Saddleback",
"Angles5color",
"BlackRedYellowWhite",
"GreyOrange",
"IntervalVelocity",
"IntervalVelocity16",
"IntervalVelocity32",
"Rainbow",
"RedGreenBlue",
"RedWhiteBlueExtremes",
"RedWhiteBlueHot",
"RedYellowBlue",
"SaddlebackHot",
"Spectrum"
],
colorMapSelect: "",
_colorMap: "",
pipeline: null,
_seismicWidget: null,
NormalizationType: null,
NormalizationBl: 0.1,
NormalizationTypeData: [
{
"label": "None",
"value": 0,
},
{
"label": "Maximum",
"value": 1,
},
{
"label": "TraceMaximum",
"value": 2,
},
{
"label": "Average",
"value": 3,
},
{
"label": "TraceAverage",
"value": 4,
}, {
"label": "RMS",
"value": 5,
}, {
"label": "TraceRMS",
"value": 6,
}, {
"label": "Limits",
"value": 7,
}
],
mycds: null,
msycs: null,
samplesType: null,
samplesEdge: null,
tracesType: null,
tracesEdge: null,
InterpolationType: [
{
"label": "Linear",
"value": 1,
}, {
"label": "Quadratic",
"value": 2,
}, {
"label": "Step",
"value": 3,
}, {
"label": "CenteredStep",
"value": 4,
}, {
"label": "Cubic",
"value": 5,
}, {
"label": "Logarithmic",
"value": 6,
},
],
InterpolationEdge: [
{
"label": "Zero",
"value": 0,
}, {
"label": "Duplicate",
"value": 1,
}
],
Wiggle: false,
Reversed: false,
PositiveFill: false,
NegativeFill: false,
PositiveColorFill: false,
NegativeColorFill: false,
SimpleDensity: false,
InterpolatedDensity: true,
ClippingFactor: 4,
DecimationSpacing: 5,
densityDecimation: false,
ClippingMode: null,
ClippingModeData: [
{
"label": "Connected",
"value": "Connected"
}, {
"label": "Disconnected",
"value": "Disconnected"
},
],
TaperFilterEnbled: false,
f1: 10,
f2: 20,
f3: 60,
f4: 70,
sampleRate: 0,
passFlag: false,
AGCEnbled: false,
AGCLength: 0,
DesiredAverage: 1,
NoiseReductionPercentage: 3,
NoiseReduction: null,
StartSample: 0,
Step: 1,
Units: "",
WindowLength: 250,
UnitsData: [
{
"label": "Sample",
"value": 0
}, {
"label": "Time",
"value": 1
},
],
NoiseReductionData: [
{
"label": "disable",
"value": "disable"
}, {
"label": "enable",
"value": "enable"
}, {
"label": "auto",
"value": "auto"
},
],
ReverseEnbled: false,
inverted: false,
reversed: false,
// 文件上传相关
uploadUrl: process.env.VUE_APP_BASE_API + '/ndy/dz/upload',
uploadHeaders: {
Authorization: "Bearer " + getToken()
},
fileList: []
}
},
mounted() {
// this.init()
},
methods: {
init() {
//创建
seismicPlot = new SeismicPlot({
'canvas': this.$refs.plot,
'errorCallback': this.onCreateWidgetError,
'fileOpenedCallback': this.onFileOpen
});
// 不再自动加载默认文件,等待用户上传文件
},
handleFileSelect() {
let url = process.env.VUE_APP_BASE_API + "/ndy/dz/getSegyDataFile3?fileName=density.segy";
let fileName = 'density.segy';
return fetch(url, {
headers: {
Authorization: "Bearer " + getToken()
}
}).then((response) => {
if (!response.ok) {
throw new Error(`网络请求失败: ${response.status} ${response.statusText}`);
}
return response.blob();
}).then((blob) => {
if (!blob || blob.size === 0) {
throw new Error('获取到的文件数据为空');
}
let fileInput = new File([blob], fileName, { type: blob.type, lastModified: Date.now() });
// seismicPlot.createWidgetFromFile(filesObj)
const file = new LocalFile(fileInput);
this._fileSize = file.fileSize;
const segyReader = new SegyReader(file);
segyReader.loadMetaData((reader) => {
if (reader instanceof Error && this.errorCallback != null) {
this.errorCallback(reader.message);
return;
}
// this.createRemotePipeline(reader)
reader.readDataSetStatistics((reader, statistics) => {
if (reader.getModelLimits().getHeight() === 0 && this.warningCallback != null) {
return this.$message.error("加载失败");
}
// return this.createPipelineFromFile(file.getFileName(), reader, statistics);
this.pipeline = new SeismicPipeline(file.getFileName(), reader, statistics)
seismicPlot.createPipelineFromFile2(this.pipeline, file.getFileName(), reader, statistics);
console.log(this.pipeline)
});
});
})
},
onCreateWidgetError(errorMsg) {
this.$message.error(errorMsg);
},
editProperties() {
// console.log(seismicPlot)
// this.ajv = getAjv({chartNames: seismicPlot.getChartNames()});
// console.log(this.ajv)
// this.schema = this.ajv.getSchema(`/${seismicPlot.getWidget().getClassName()}`).schema;
// console.log(this.schema)
// this.nodeProps = getNodeProps(seismicPlot);
// console.log(this.nodeProps)
this.showPropertiesDialog = true;
},
applyProps(props) {
if (props) {
setNodeProps(seismicPlot, props);
}
this.showPropertiesDialog = false;
},
onFileOpen() {
},
changeColor(val) {
var colorMap = SeismicColors.getDefault().createNamedColorMap(val, 256)
this.pipeline.setColorMap(colorMap);
if (seismicPlot._seismicWidget) {
seismicPlot._seismicWidget.invalidate(); // 使组件重新渲染
seismicPlot._seismicWidget.fitToBounds(); // 调整视图以适应数据
} else {
console.error('Widget不可用,无法更新视图');
}
// 如果plots对象存在redraw方法,使用它来触发重绘
if (seismicPlot.plots && typeof seismicPlot.plots.redraw === 'function') {
seismicPlot.plots.redraw();
}
},
//归一化
changeNor(val) {
if (this.NormalizationType == 7) {
this.pipeline.setOptions({
'normalization': {
'type': this.NormalizationType,
'scale': this.NormalizationBl
}
})
} else {
this.pipeline.setOptions({
'normalization': {
'type': this.NormalizationType,
'scale': this.NormalizationBl
}
})
}
},
changeMycds() {
seismicPlot.openPipeline(this.pipeline, {
'tracescale': this.mycds,
'samplescale': this.msycs,
'deviceunit': 'in',
'sampleunit': 's',
});
},
changeInt() {
console.log(this.pipeline.getOptions())
this.pipeline.setOptions({
'interpolation': {
'samples': {
"type": this.samplesType,//采样插值类型,
"edge": this.samplesEdge,//采样插值边缘,
},
'traces': {
"type": this.tracesType,//道插值类型
"edge": this.tracesEdge,//道插值边缘
},
}
})
},
changePlotType() {
this.pipeline.setOptions({
'plot': {
'type': {
"Wiggle": this.Wiggle,
"Reversed": this.Reversed,
"PositiveFill": this.PositiveFill,
"NegativeFill": this.NegativeFill,
"PositiveColorFill": this.PositiveColorFill,
"NegativeColorFill": this.NegativeColorFill,
"SimpleDensity": this.SimpleDensity,
"InterpolatedDensity": this.InterpolatedDensity,
},
"clippingFactor": this.ClippingFactor,
"decimationSpacing": this.DecimationSpacing,
"densityDecimation": this.densityDecimation,
},
"clippingmode": this.ClippingMode,
"dataProcessors": {
"TaperFilter": {
"apply": this.TaperFilterEnbled,
"f1": Number(this.f1),
"f2": Number(this.f2),
"f3": Number(this.f3),
"f4": Number(this.f4),
"sampleRate": Number(this.sampleRate),
"passFlag": this.passFlag,
},
"AGC": {
"agcLength": this.AGCLength,
"apply": this.AGCEnbled,
"desiredAverage": this.DesiredAverage,
"noiseReduction": this.NoiseReduction,
"noiseReductionPercentage": this.NoiseReductionPercentage,
"startSample": this.StartSample,
"step": this.Step,
"units": this.Units,
"windowLength": this.WindowLength,
},
"Reverse": {
"apply": this.ReverseEnbled,
"inverted": this.inverted,
"reversed": this.reversed,
}
}
})
console.log(this.pipeline.getOptions())
},
// 文件上传相关方法
triggerFileUpload() {
// 创建隐藏的文件输入元素
const input = document.createElement('input');
input.type = 'file';
input.accept = '.segy,.sgy';
input.style.display = 'none';
input.onchange = (event) => {
const file = event.target.files[0];
if (file) {
this.handleFileUpload(file);
}
};
document.body.appendChild(input);
input.click();
document.body.removeChild(input);
},
handleFileUpload(file) {
// 检查文件类型
const allowedTypes = ['.segy', '.sgy'];
const fileName = file.name.toLowerCase();
const isValidType = allowedTypes.some(type => fileName.endsWith(type));
if (!isValidType) {
this.$message.error('只能上传segy、sgy文件');
return;
}
// 检查文件大小 (例如限制为100MB)
const maxSize = 100 * 1024 * 1024; // 100MB
if (file.size > maxSize) {
this.$message.error('文件大小不能超过100MB');
return;
}
// 创建FormData
const formData = new FormData();
formData.append('file', file);
// 上传文件
this.$message.info('正在上传文件...');
fetch(this.uploadUrl, {
method: 'POST',
headers: this.uploadHeaders,
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error(`上传失败: ${response.status} ${response.statusText}`);
}
return response.json();
})
.then(data => {
this.$message.success('文件上传成功');
console.log('上传成功:', data);
// 这里可以处理上传成功后的逻辑,比如重新加载数据
this.loadNewFile(data.fileName);
this.init()
})
.catch(error => {
console.error('上传失败:', error);
this.$message.error('文件上传失败: ' + error.message);
});
},
loadNewFile(fileName) {
// 加载新文件的逻辑
let url = process.env.VUE_APP_BASE_API + "/ndy/dz/getSegyDataFile3?fileName=" + fileName;
return fetch(url, {
headers: {
Authorization: "Bearer " + getToken()
}
}).then((response) => {
if (!response.ok) {
throw new Error(`网络请求失败: ${response.status} ${response.statusText}`);
}
return response.blob();
}).then((blob) => {
if (!blob || blob.size === 0) {
throw new Error('获取到的文件数据为空');
}
let fileInput = new File([blob], fileName, { type: blob.type, lastModified: Date.now() });
const file = new LocalFile(fileInput);
this._fileSize = file.fileSize;
const segyReader = new SegyReader(file);
segyReader.loadMetaData((reader) => {
if (reader instanceof Error && this.errorCallback != null) {
this.errorCallback(reader.message);
return;
}
reader.readDataSetStatistics((reader, statistics) => {
if (reader.getModelLimits().getHeight() === 0 && this.warningCallback != null) {
return this.$message.error("加载失败");
}
this.pipeline = new SeismicPipeline(fileName, reader, statistics)
seismicPlot.createPipelineFromFile2(this.pipeline, fileName, reader, statistics);
console.log(this.pipeline)
});
});
}).catch(error => {
console.error('加载文件失败:', error);
this.$message.error('加载文件失败: ' + error.message);
});
}
}
}
</script>
<style scoped>
#canvas {
width: 100% !important;
height: calc(85vh - 35px) !important;
margin-top: 20px;
}
</style>
<!-- 成果列表 --> <!-- 成果列表 -->
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- 左右分栏布局 --> <!-- 左右分栏布局 -->
<div class="main-layout" :class="{ 'viewer-mode': isViewerRole }"> <div class="main-layout" :class="{ 'viewer-mode': isViewerRole }">
<!-- 左侧:文件上传情况 --> <!-- 左侧:文件上传情况 -->
<div class="left-panel"> <div class="left-panel">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="文件名称" prop="wjmc"> <el-form-item label="文件名称" prop="wjmc">
<el-input v-model="queryParams.wjmc" placeholder="请输入文件名称" clearable @keyup.enter.native="handleQuery" /> <el-input v-model="queryParams.wjmc" placeholder="请输入文件名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="资料类型" prop="zllx"> <el-form-item label="资料类型" prop="zllx">
<el-select v-model="queryParams.zllx" placeholder="请选择资料类型" clearable> <el-select v-model="queryParams.zllx" placeholder="请选择资料类型" clearable>
<el-option <el-option
v-for="item in zllxOptions" v-for="item in zllxOptions"
:key="item.id" :key="item.id"
:label="item.lxmc" :label="item.lxmc"
:value="item.lxmc"> :value="item.lxmc">
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button v-if="!isViewerRole" type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" <el-button v-if="!isViewerRole" type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['yscgWjscqk:wjscqk:add']">新增</el-button> v-hasPermi="['yscgWjscqk:wjscqk:add']">新增</el-button>
<el-button v-if="!isViewerRole" type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" <el-button v-if="!isViewerRole" type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate"
v-hasPermi="['yscgWjscqk:wjscqk:edit']">修改</el-button> v-hasPermi="['yscgWjscqk:wjscqk:edit']">修改</el-button>
<el-button v-if="!isViewerRole" type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" <el-button v-if="!isViewerRole" type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete"
v-hasPermi="['yscgWjscqk:wjscqk:remove']">删除</el-button> v-hasPermi="['yscgWjscqk:wjscqk:remove']">删除</el-button>
<el-button v-if="!isViewerRole" type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" <el-button v-if="!isViewerRole" type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
v-hasPermi="['yscgWjscqk:wjscqk:export']">导出</el-button> v-hasPermi="['yscgWjscqk:wjscqk:export']">导出</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table border v-loading="loading" :data="wjscqkList" @selection-change="handleSelectionChange"> <el-table border v-loading="loading" :data="wjscqkList" @selection-change="handleSelectionChange">
<el-table-column v-if="!isViewerRole" type="selection" width="55" align="center" /> <el-table-column v-if="!isViewerRole" type="selection" width="55" align="center" />
<el-table-column label="文件名称" align="center" prop="wjmc" min-width="120" show-overflow-tooltip> <el-table-column label="文件名称" align="center" prop="wjmc" min-width="120" show-overflow-tooltip>
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" @click="handleDownload(scope.row)" style="padding: 0; color: #409EFF;"> <el-button type="text" @click="handleDownload(scope.row)" style="padding: 0; color: #409EFF;">
{{ scope.row.wjmc }} {{ scope.row.wjmc }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="资料类型" align="center" prop="zllx" min-width="100" show-overflow-tooltip /> <el-table-column label="资料类型" align="center" prop="zllx" min-width="100" show-overflow-tooltip />
<el-table-column label="上传时间" align="center" prop="createdTime" min-width="170" show-overflow-tooltip /> <el-table-column label="上传时间" align="center" prop="createdTime" min-width="170" show-overflow-tooltip />
<el-table-column v-if="!isViewerRole" label="操作" min-width="110" align="center" class-name="small-padding fixed-width"> <el-table-column v-if="!isViewerRole" label="操作" min-width="110" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['yscgWjscqk:wjscqk:edit']">修改</el-button> v-hasPermi="['yscgWjscqk:wjscqk:edit']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['yscgWjscqk:wjscqk:remove']">删除</el-button> v-hasPermi="['yscgWjscqk:wjscqk:remove']">删除</el-button>
<el-button size="mini" type="text" icon="el-icon-share" @click="handleShare(scope.row)" <el-button size="mini" type="text" icon="el-icon-share" @click="handleShare(scope.row)"
v-hasPermi="['yscgWjscqk:wjscqk:share']">分享</el-button> v-hasPermi="['yscgWjscqk:wjscqk:share']">分享</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" /> @pagination="getList" />
</div> </div>
<!-- 右侧:项目成员 --> <!-- 右侧:项目成员 -->
<div v-if="!isViewerRole" class="right-panel"> <div v-if="!isViewerRole" class="right-panel">
<div class="xmcy-panel"> <div class="xmcy-panel">
<!-- 标题和操作栏 --> <!-- 标题和操作栏 -->
<div class="xmcy-header"> <div class="xmcy-header">
<h3 class="xmcy-title">项目成员 ({{ xmcyTotal }})</h3> <h3 class="xmcy-title">项目成员 ({{ xmcyTotal }})</h3>
<div class="xmcy-actions"> <div class="xmcy-actions">
<el-button <el-button
type="primary" type="primary"
icon="el-icon-plus" icon="el-icon-plus"
size="small" size="small"
@click="handleXmcyAdd" @click="handleXmcyAdd"
class="invite-btn" class="invite-btn"
>邀请作成员</el-button> >邀请作成员</el-button>
<el-button <el-button
type="text" type="text"
icon="el-icon-search" icon="el-icon-search"
size="small" size="small"
@click="toggleSearch" @click="toggleSearch"
class="search-btn" class="search-btn"
></el-button> ></el-button>
</div> </div>
</div> </div>
<!-- 搜索框 --> <!-- 搜索框 -->
<div v-show="showSearchBox" class="xmcy-search"> <div v-show="showSearchBox" class="xmcy-search">
<el-input <el-input
v-model="xmcyQueryParams.nickName" v-model="xmcyQueryParams.nickName"
placeholder="搜索成员" placeholder="搜索成员"
clearable clearable
prefix-icon="el-icon-search" prefix-icon="el-icon-search"
@keyup.enter.native="handleXmcyQuery" @keyup.enter.native="handleXmcyQuery"
@clear="handleXmcyQuery" @clear="handleXmcyQuery"
size="small" size="small"
> >
</el-input> </el-input>
</div> </div>
<!-- 项目成员列表 --> <!-- 项目成员列表 -->
<div class="xmcy-list" v-loading="xmcyLoading"> <div class="xmcy-list" v-loading="xmcyLoading">
<div <div
v-for="item in xmcyList" v-for="item in xmcyList"
:key="item.cyid" :key="item.cyid"
class="xmcy-item" class="xmcy-item"
> >
<div class="xmcy-item-left"> <div class="xmcy-item-left">
<!-- 头像图标 --> <!-- 头像图标 -->
<div class="xmcy-avatar avatar-member"> <div class="xmcy-avatar avatar-member">
<i class="el-icon-user"></i> <i class="el-icon-user"></i>
</div> </div>
<div class="xmcy-info"> <div class="xmcy-info">
<!-- 角色标签 --> <!-- 角色标签 -->
<div class="xmcy-role-tag"> <div class="xmcy-role-tag">
<dict-tag :options="dict.type.xm_js" :value="item.xmjs"/> <dict-tag :options="dict.type.xm_js" :value="item.xmjs"/>
</div> </div>
<!-- 用户姓名 --> <!-- 用户姓名 -->
<div class="xmcy-name">{{ item.nickName }}</div> <div class="xmcy-name">{{ item.nickName }}</div>
</div> </div>
</div> </div>
<div class="xmcy-item-right"> <div class="xmcy-item-right">
<el-dropdown trigger="click" @command="handleCommand"> <el-dropdown trigger="click" @command="handleCommand">
<span class="el-dropdown-link"> <span class="el-dropdown-link">
<i class="el-icon-arrow-down"></i> <i class="el-icon-arrow-down"></i>
</span> </span>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{action: 'edit', row: item}"> <el-dropdown-item :command="{action: 'edit', row: item}">
<i class="el-icon-edit"></i> 修改 <i class="el-icon-edit"></i> 修改
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item divided> <el-dropdown-item divided>
<el-popconfirm <el-popconfirm
title="是否确认删除该成员?" title="是否确认删除该成员?"
@confirm="handleXmcyDelete(item)" @confirm="handleXmcyDelete(item)"
placement="top" placement="top"
> >
<span slot="reference" style="display: block; width: 100%;"> <span slot="reference" style="display: block; width: 100%;">
<i class="el-icon-delete"></i> 删除 <i class="el-icon-delete"></i> 删除
</span> </span>
</el-popconfirm> </el-popconfirm>
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</div> </div>
</div> </div>
<!-- 空状态 --> <!-- 空状态 -->
<div v-if="!xmcyLoading && xmcyList.length === 0" class="xmcy-empty"> <div v-if="!xmcyLoading && xmcyList.length === 0" class="xmcy-empty">
<p>暂无成员</p> <p>暂无成员</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- 添加或修改成果管理对话框 --> <!-- 添加或修改成果管理对话框 -->
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body> <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="89px"> <el-form ref="form" :model="form" :rules="rules" label-width="89px">
<el-form-item label="资料类型" prop="zllx"> <el-form-item label="资料类型" prop="zllx">
<el-select v-model="form.zllx" placeholder="请选择资料类型" clearable> <el-select v-model="form.zllx" placeholder="请选择资料类型" clearable>
<el-option <el-option
v-for="item in zllxOptions" v-for="item in zllxOptions"
:key="item.id" :key="item.id"
:label="item.lxmc" :label="item.lxmc"
:value="item.lxmc"> :value="item.lxmc">
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="文件上传" prop="fileList"> <el-form-item label="文件上传" prop="fileList">
<el-upload <el-upload
ref="fileUpload" ref="fileUpload"
:action="uploadUrl" :action="uploadUrl"
:headers="uploadHeaders" :headers="uploadHeaders"
:file-list="fileList" :file-list="fileList"
:before-upload="handleBeforeUpload" :before-upload="handleBeforeUpload"
:on-success="handleUploadSuccess" :on-success="handleUploadSuccess"
:on-error="handleUploadError" :on-error="handleUploadError"
:on-remove="handleRemove" :on-remove="handleRemove"
:on-exceed="handleExceed" :on-exceed="handleExceed"
:on-change="handleFileChange" :on-change="handleFileChange"
:data="uploadData" :data="uploadData"
drag drag
class="upload-dragger" class="upload-dragger"
> >
<i class="el-icon-upload"></i> <i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip"> <div class="el-upload__tip" slot="tip">
支持 doc、docx、pdf、xls、xlsx、ppt、pptx、txt、jpg、jpeg、png、gif 格式文件,单个文件不超过50MB<br/> 支持 doc、docx、pdf、xls、xlsx、ppt、pptx、txt、jpg、jpeg、png、gif 格式文件<br/>
<span style="color: #409EFF;">上传新文件将自动替换已有文件</span> <span style="color: #409EFF;">上传新文件将自动替换已有文件</span>
</div> </div>
</el-upload> </el-upload>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button> <el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button> <el-button @click="cancel">取 消</el-button>
</div> </div>
</el-dialog> </el-dialog>
<!-- 添加或修改项目成员对话框 --> <!-- 添加或修改项目成员对话框 -->
<el-dialog :title="xmcyTitle" :visible.sync="xmcyOpen" width="500px" append-to-body> <el-dialog :title="xmcyTitle" :visible.sync="xmcyOpen" width="500px" append-to-body>
<el-form ref="xmcyForm" :model="xmcyForm" :rules="xmcyRules" label-width="80px"> <el-form ref="xmcyForm" :model="xmcyForm" :rules="xmcyRules" label-width="80px">
<el-form-item label="用户姓名" prop="nickName"> <el-form-item label="用户姓名" prop="nickName">
<el-input v-model="xmcyForm.nickName" placeholder="请选择用户" readonly> <el-input v-model="xmcyForm.nickName" placeholder="请选择用户" readonly>
<el-button slot="append" icon="el-icon-search" @click="openUserDialog"></el-button> <el-button slot="append" icon="el-icon-search" @click="openUserDialog"></el-button>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item label="项目角色" prop="xmjs"> <el-form-item label="项目角色" prop="xmjs">
<el-select style="width: 100%;" v-model="xmcyForm.xmjs" placeholder="请选择项目角色"> <el-select style="width: 100%;" v-model="xmcyForm.xmjs" placeholder="请选择项目角色">
<el-option <el-option
v-for="dict in dict.type.xm_js" v-for="dict in dict.type.xm_js"
:key="dict.value" :key="dict.value"
:label="dict.label" :label="dict.label"
:value="dict.value" :value="dict.value"
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitXmcyForm">确 定</el-button> <el-button type="primary" @click="submitXmcyForm">确 定</el-button>
<el-button @click="cancelXmcy">取 消</el-button> <el-button @click="cancelXmcy">取 消</el-button>
</div> </div>
</el-dialog> </el-dialog>
<!-- 选择用户对话框 --> <!-- 选择用户对话框 -->
<el-dialog title="选择用户" :visible.sync="userDialogVisible" width="800px" append-to-body> <el-dialog title="选择用户" :visible.sync="userDialogVisible" width="800px" append-to-body>
<el-form :model="userQueryParams" ref="userQueryForm" size="small" :inline="true" label-width="80px"> <el-form :model="userQueryParams" ref="userQueryForm" size="small" :inline="true" label-width="80px">
<el-form-item label="统一账号" prop="userName"> <el-form-item label="统一账号" prop="userName">
<el-input <el-input
v-model="userQueryParams.userName" v-model="userQueryParams.userName"
placeholder="请输入统一账号" placeholder="请输入统一账号"
clearable clearable
size="small" size="small"
style="width: 200px" style="width: 200px"
@keyup.enter.native="handleUserSearch" @keyup.enter.native="handleUserSearch"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleUserSearch">搜索</el-button> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleUserSearch">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetUserQuery">重置</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetUserQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table <el-table
v-loading="userLoading" v-loading="userLoading"
:data="userList" :data="userList"
@row-click="handleUserSelect" @row-click="handleUserSelect"
highlight-current-row highlight-current-row
style="cursor: pointer;" style="cursor: pointer;"
> >
<el-table-column label="统一账号" align="center" prop="userName" /> <el-table-column label="统一账号" align="center" prop="userName" />
<el-table-column label="用户姓名" align="center" prop="nickName" /> <el-table-column label="用户姓名" align="center" prop="nickName" />
<el-table-column label="部门" align="center" prop="dept.deptName" /> <el-table-column label="部门" align="center" prop="dept.deptName" />
</el-table> </el-table>
<pagination <pagination
v-show="userTotal>0" v-show="userTotal>0"
:total="userTotal" :total="userTotal"
:page.sync="userQueryParams.pageNum" :page.sync="userQueryParams.pageNum"
:limit.sync="userQueryParams.pageSize" :limit.sync="userQueryParams.pageSize"
@pagination="getUserList" @pagination="getUserList"
/> />
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { listWjscqk, getWjscqk, delWjscqk, addWjscqk, updateWjscqk } from "@/api/yscgWjscqk/wjscqk" import { listWjscqk, getWjscqk, delWjscqk, addWjscqk, updateWjscqk } from "@/api/yscgWjscqk/wjscqk"
import { selectZllx } from "@/api/yscgZllx/zllx" import { selectZllx } from "@/api/yscgZllx/zllx"
import { getToken } from "@/utils/auth" import { getToken } from "@/utils/auth"
import { listXmcy, getXmcy, delXmcy, addXmcy, updateXmcy } from "@/api/ysqqXmxxXmcy/xmcy" import { listXmcy, getXmcy, delXmcy, addXmcy, updateXmcy } from "@/api/ysqqXmxxXmcy/xmcy"
import { listUser } from "@/api/system/user" import { listUser } from "@/api/system/user"
export default { export default {
name: "Wjscqk", name: "Wjscqk",
dicts: ['xm_js'], dicts: ['xm_js'],
data() { data() {
return { return {
// 遮罩层 // 遮罩层
loading: true, loading: true,
// 选中数组 // 选中数组
ids: [], ids: [],
// 非单个禁用 // 非单个禁用
single: true, single: true,
// 非多个禁用 // 非多个禁用
multiple: true, multiple: true,
// 显示搜索条件 // 显示搜索条件
showSearch: true, showSearch: true,
// 总条数 // 总条数
total: 0, total: 0,
// 成果管理表格数据 // 成果管理表格数据
wjscqkList: [], wjscqkList: [],
// 弹出层标题 // 弹出层标题
title: "", title: "",
// 是否显示弹出层 // 是否显示弹出层
open: false, open: false,
// 查询参数 // 查询参数
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
id: null, id: null,
xmid: null, xmid: null,
wjmc: null, wjmc: null,
zllx: null, zllx: null,
wjlj: null, wjlj: null,
createdBy: null, createdBy: null,
createdTime: null, createdTime: null,
ext1: null, ext1: null,
ext2: null, ext2: null,
ext3: null ext3: null
}, },
// 表单参数 // 表单参数
form: {}, form: {},
// 表单校验 // 表单校验
rules: { rules: {
}, },
// 资料类型选项 // 资料类型选项
zllxOptions: [], zllxOptions: [],
// 文件列表 // 文件列表
fileList: [], fileList: [],
// 上传配置 // 上传配置
uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload", uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload",
uploadHeaders: { uploadHeaders: {
Authorization: "Bearer " + getToken() Authorization: "Bearer " + getToken()
}, },
uploadData: {}, uploadData: {},
// 项目成员相关数据 // 项目成员相关数据
// 项目成员列表加载状态 // 项目成员列表加载状态
xmcyLoading: false, xmcyLoading: false,
// 项目成员列表 // 项目成员列表
xmcyList: [], xmcyList: [],
// 项目成员总条数 // 项目成员总条数
xmcyTotal: 0, xmcyTotal: 0,
// 项目成员查询参数 // 项目成员查询参数
xmcyQueryParams: { xmcyQueryParams: {
pageNum: 1, pageNum: 1,
pageSize: 9999, pageSize: 9999,
nickName: null, nickName: null,
zbid: null zbid: null
}, },
// 项目成员表单参数 // 项目成员表单参数
xmcyForm: {}, xmcyForm: {},
// 项目成员表单校验 // 项目成员表单校验
xmcyRules: { xmcyRules: {
nickName: [ nickName: [
{ required: true, message: "用户姓名不能为空", trigger: "change" } { required: true, message: "用户姓名不能为空", trigger: "change" }
], ],
xmjs: [ xmjs: [
{ required: true, message: "项目角色不能为空", trigger: "change" } { required: true, message: "项目角色不能为空", trigger: "change" }
] ]
}, },
// 项目成员弹出层标题 // 项目成员弹出层标题
xmcyTitle: "", xmcyTitle: "",
// 是否显示项目成员弹出层 // 是否显示项目成员弹出层
xmcyOpen: false, xmcyOpen: false,
// 选择用户对话框 // 选择用户对话框
userDialogVisible: false, userDialogVisible: false,
// 用户列表加载状态 // 用户列表加载状态
userLoading: false, userLoading: false,
// 用户列表 // 用户列表
userList: [], userList: [],
// 用户列表总条数 // 用户列表总条数
userTotal: 0, userTotal: 0,
// 用户查询参数 // 用户查询参数
userQueryParams: { userQueryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
userName: null userName: null
}, },
// 是否显示搜索框 // 是否显示搜索框
showSearchBox: false, showSearchBox: false,
// 当前用户的项目角色 // 当前用户的项目角色
currentUserRole: null, currentUserRole: null,
// 是否为查看者角色 // 是否为查看者角色
isViewerRole: false isViewerRole: false
} }
}, },
created() { created() {
// 获取路由参数中的id和角色 // 获取路由参数中的id和角色
if (this.$route.query.xmid) { if (this.$route.query.xmid) {
this.queryParams.xmid = this.$route.query.xmid this.queryParams.xmid = this.$route.query.xmid
// 设置项目成员查询的zbid(xmid对应zbid) // 设置项目成员查询的zbid(xmid对应zbid)
this.xmcyQueryParams.zbid = this.$route.query.xmid this.xmcyQueryParams.zbid = this.$route.query.xmid
} }
// 获取路由参数中的项目角色 // 获取路由参数中的项目角色
if (this.$route.query.xmjs) { if (this.$route.query.xmjs) {
this.currentUserRole = this.$route.query.xmjs this.currentUserRole = this.$route.query.xmjs
} }
this.getList() this.getList()
this.getZllxOptions() this.getZllxOptions()
}, },
mounted() { mounted() {
// 在字典加载完成后判断角色 // 在字典加载完成后判断角色
this.$nextTick(() => { this.$nextTick(() => {
if (this.dict && this.dict.type && this.dict.type.xm_js) { if (this.dict && this.dict.type && this.dict.type.xm_js) {
this.checkViewerRole() this.checkViewerRole()
} }
// 加载项目成员列表 // 加载项目成员列表
if (this.xmcyQueryParams.zbid && !this.isViewerRole) { if (this.xmcyQueryParams.zbid && !this.isViewerRole) {
this.getXmcyList() this.getXmcyList()
} }
}) })
}, },
watch: { watch: {
// 监听路由参数变化 // 监听路由参数变化
'$route.query.xmid'(newId) { '$route.query.xmid'(newId) {
if (newId) { if (newId) {
this.queryParams.xmid = newId this.queryParams.xmid = newId
this.xmcyQueryParams.zbid = newId this.xmcyQueryParams.zbid = newId
this.getList() this.getList()
if (!this.isViewerRole) { if (!this.isViewerRole) {
this.getXmcyList() this.getXmcyList()
} }
} }
}, },
// 监听路由参数中的角色变化 // 监听路由参数中的角色变化
'$route.query.xmjs'(newRole) { '$route.query.xmjs'(newRole) {
if (newRole) { if (newRole) {
this.currentUserRole = newRole this.currentUserRole = newRole
} }
}, },
// 监听字典数据变化,字典加载完成后再判断角色 // 监听字典数据变化,字典加载完成后再判断角色
'dict.type.xm_js'() { 'dict.type.xm_js'() {
if (this.dict && this.dict.type && this.dict.type.xm_js) { if (this.dict && this.dict.type && this.dict.type.xm_js) {
this.checkViewerRole() this.checkViewerRole()
} }
}, },
// 监听当前用户角色变化 // 监听当前用户角色变化
currentUserRole() { currentUserRole() {
this.checkViewerRole() this.checkViewerRole()
} }
}, },
methods: { methods: {
/** 查询成果管理列表 */ /** 查询成果管理列表 */
getList() { getList() {
this.loading = true this.loading = true
listWjscqk(this.queryParams).then(response => { listWjscqk(this.queryParams).then(response => {
const mergedRows = this.mergeRowsById(response.rows || []) const mergedRows = this.mergeRowsById(response.rows || [])
this.wjscqkList = mergedRows this.wjscqkList = mergedRows
this.total = mergedRows.length this.total = mergedRows.length
this.loading = false this.loading = false
}) })
}, },
/** 将相同id的记录进行合并(合并文件名与文件路径) */ /** 将相同id的记录进行合并(合并文件名与文件路径) */
mergeRowsById(rows) { mergeRowsById(rows) {
const idToRowMap = {} const idToRowMap = {}
const mergeCommaSeparated = (first, second) => { const mergeCommaSeparated = (first, second) => {
const parts = [] const parts = []
if (first) parts.push(...String(first).split(',').filter(Boolean)) if (first) parts.push(...String(first).split(',').filter(Boolean))
if (second) parts.push(...String(second).split(',').filter(Boolean)) if (second) parts.push(...String(second).split(',').filter(Boolean))
const seen = new Set() const seen = new Set()
return parts.filter(item => { return parts.filter(item => {
if (seen.has(item)) return false if (seen.has(item)) return false
seen.add(item) seen.add(item)
return true return true
}).join(',') || null }).join(',') || null
} }
rows.forEach(row => { rows.forEach(row => {
const key = row.id const key = row.id
if (key == null) { if (key == null) {
// 无id的记录直接保留为独立项 // 无id的记录直接保留为独立项
const tempKey = `__no_id__${Math.random()}_${Date.now()}` const tempKey = `__no_id__${Math.random()}_${Date.now()}`
idToRowMap[tempKey] = { ...row } idToRowMap[tempKey] = { ...row }
return return
} }
if (!idToRowMap[key]) { if (!idToRowMap[key]) {
idToRowMap[key] = { ...row } idToRowMap[key] = { ...row }
} else { } else {
const existing = idToRowMap[key] const existing = idToRowMap[key]
existing.wjmc = mergeCommaSeparated(existing.wjmc, row.wjmc) existing.wjmc = mergeCommaSeparated(existing.wjmc, row.wjmc)
existing.wjlj = mergeCommaSeparated(existing.wjlj, row.wjlj) existing.wjlj = mergeCommaSeparated(existing.wjlj, row.wjlj)
// 其它字段保持首次出现的值,或可在此按需处理 // 其它字段保持首次出现的值,或可在此按需处理
} }
}) })
return Object.values(idToRowMap) return Object.values(idToRowMap)
}, },
// 取消按钮 // 取消按钮
cancel() { cancel() {
this.open = false this.open = false
this.reset() this.reset()
}, },
// 表单重置 // 表单重置
reset() { reset() {
this.form = { this.form = {
id: null, id: null,
xmid: null, xmid: null,
wjmc: null, wjmc: null,
zllx: null, zllx: null,
wjlj: null, wjlj: null,
createdBy: null, createdBy: null,
createdTime: null, createdTime: null,
updateBy: null, updateBy: null,
updateTime: null, updateTime: null,
ext1: null, ext1: null,
ext2: null, ext2: null,
ext3: null ext3: null
} }
this.fileList = [] this.fileList = []
this.resetForm("form") this.resetForm("form")
}, },
/** 搜索按钮操作 */ /** 搜索按钮操作 */
handleQuery() { handleQuery() {
this.queryParams.pageNum = 1 this.queryParams.pageNum = 1
this.getList() this.getList()
}, },
/** 重置按钮操作 */ /** 重置按钮操作 */
resetQuery() { resetQuery() {
this.resetForm("queryForm") this.resetForm("queryForm")
this.handleQuery() this.handleQuery()
}, },
// 多选框选中数据 // 多选框选中数据
handleSelectionChange(selection) { handleSelectionChange(selection) {
this.ids = selection.map(item => item.id) this.ids = selection.map(item => item.id)
this.single = selection.length !== 1 this.single = selection.length !== 1
this.multiple = !selection.length this.multiple = !selection.length
}, },
/** 新增按钮操作 */ /** 新增按钮操作 */
handleAdd() { handleAdd() {
this.reset() this.reset()
// 设置xmid参数 // 设置xmid参数
if (this.queryParams.xmid) { if (this.queryParams.xmid) {
this.form.xmid = this.queryParams.xmid this.form.xmid = this.queryParams.xmid
} }
this.open = true this.open = true
this.title = "添加成果管理" this.title = "添加成果管理"
}, },
/** 修改按钮操作 */ /** 修改按钮操作 */
handleUpdate(row) { handleUpdate(row) {
this.reset() this.reset()
const id = row.id || this.ids const id = row.id || this.ids
getWjscqk(id).then(response => { getWjscqk(id).then(response => {
this.form = response.data this.form = response.data
// 确保xmid参数被保持 // 确保xmid参数被保持
if (this.queryParams.xmid) { if (this.queryParams.xmid) {
this.form.xmid = this.queryParams.xmid this.form.xmid = this.queryParams.xmid
} }
// 如果有文件信息,加载到文件列表中(单文件) // 如果有文件信息,加载到文件列表中(单文件)
if (this.form.wjmc && this.form.wjlj) { if (this.form.wjmc && this.form.wjlj) {
this.fileList = [{ this.fileList = [{
name: this.form.wjmc, name: this.form.wjmc,
url: this.form.wjlj, url: this.form.wjlj,
status: 'success', status: 'success',
uid: Date.now() uid: Date.now()
}] }]
} }
this.open = true this.open = true
this.title = "修改成果管理" this.title = "修改成果管理"
}) })
}, },
/** 提交按钮 */ /** 提交按钮 */
submitForm() { submitForm() {
this.$refs["form"].validate(valid => { this.$refs["form"].validate(valid => {
if (valid) { if (valid) {
if (this.form.id != null) { if (this.form.id != null) {
updateWjscqk(this.form).then(response => { updateWjscqk(this.form).then(response => {
this.$modal.msgSuccess("修改成功") this.$modal.msgSuccess("修改成功")
this.open = false this.open = false
this.getList() this.getList()
}) })
} else { } else {
addWjscqk(this.form).then(response => { addWjscqk(this.form).then(response => {
this.$modal.msgSuccess("新增成功") this.$modal.msgSuccess("新增成功")
this.open = false this.open = false
this.getList() this.getList()
}) })
} }
} }
}) })
}, },
/** 删除按钮操作 */ /** 删除按钮操作 */
handleDelete(row) { handleDelete(row) {
const ids = row.id || this.ids const ids = row.id || this.ids
this.$modal.confirm('是否确认删除成果管理编号为"' + ids + '"的数据项?').then(function () { this.$modal.confirm('是否确认删除成果管理编号为"' + ids + '"的数据项?').then(function () {
return delWjscqk(ids) return delWjscqk(ids)
}).then(() => { }).then(() => {
this.getList() this.getList()
this.$modal.msgSuccess("删除成功") this.$modal.msgSuccess("删除成功")
}).catch(() => { }) }).catch(() => { })
}, },
/** 分享按钮操作 */ /** 分享按钮操作 */
handleShare(row) { handleShare(row) {
// 跳转到文件权限管理页面,传递文件ID // 跳转到文件权限管理页面,传递文件ID
const fileId = row.id const fileId = row.id
this.$router.push({ this.$router.push({
path: '/yscgFileRole/filerole', path: '/yscgFileRole/filerole',
query: { query: {
fileId: fileId, fileId: fileId,
wjmc: row.wjmc wjmc: row.wjmc
} }
}) })
}, },
/** 导出按钮操作 */ /** 导出按钮操作 */
handleExport() { handleExport() {
this.download('yscgWjscqk/wjscqk/export', { this.download('yscgWjscqk/wjscqk/export', {
...this.queryParams ...this.queryParams
}, `wjscqk_${new Date().getTime()}.xlsx`) }, `wjscqk_${new Date().getTime()}.xlsx`)
}, },
/** 获取资料类型选项 */ /** 获取资料类型选项 */
getZllxOptions() { getZllxOptions() {
selectZllx().then(response => { selectZllx().then(response => {
this.zllxOptions = response.rows || [] this.zllxOptions = response.rows || []
}).catch(() => { }).catch(() => {
this.zllxOptions = [] this.zllxOptions = []
}) })
}, },
/** 上传前校验 */ /** 上传前校验 */
handleBeforeUpload(file) { handleBeforeUpload(file) {
// 校验文件类型 // 校验文件类型
const allowedTypes = ['doc', 'docx', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'jpg', 'jpeg', 'png', 'gif'] const allowedTypes = ['doc', 'docx', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'jpg', 'jpeg', 'png', 'gif']
const fileExt = file.name.split('.').pop().toLowerCase() const fileExt = file.name.split('.').pop().toLowerCase()
if (!allowedTypes.includes(fileExt)) { if (!allowedTypes.includes(fileExt)) {
this.$modal.msgError('文件格式不正确,请上传支持的文件格式!') this.$modal.msgError('文件格式不正确,请上传支持的文件格式!')
return false return false
} }
// 校验文件大小 // 校验文件大小
const isLt50M = file.size / 1024 / 1024 < 50 // const isLt50M = file.size / 1024 / 1024 < 50
if (!isLt50M) { // if (!isLt50M) {
this.$modal.msgError('上传文件大小不能超过 50MB!') // this.$modal.msgError('上传文件大小不能超过 50MB!')
return false // return false
} // }
return true return true
}, },
/** 文件状态改变 */ /** 文件状态改变 */
handleFileChange(file, fileList) { handleFileChange(file, fileList) {
// 如果文件列表超过1个,只保留最新的文件 // 如果文件列表超过1个,只保留最新的文件
if (fileList.length > 1) { if (fileList.length > 1) {
this.fileList = [fileList[fileList.length - 1]] this.fileList = [fileList[fileList.length - 1]]
} else { } else {
this.fileList = fileList this.fileList = fileList
} }
this.updateFormFiles() this.updateFormFiles()
}, },
/** 上传成功 */ /** 上传成功 */
handleUploadSuccess(response, file, fileList) { handleUploadSuccess(response, file, fileList) {
this.fileList = fileList this.fileList = fileList
this.updateFormFiles() this.updateFormFiles()
this.$modal.msgSuccess('文件上传成功') this.$modal.msgSuccess('文件上传成功')
}, },
/** 上传失败 */ /** 上传失败 */
handleUploadError(err, file, fileList) { handleUploadError(err, file, fileList) {
this.$modal.msgError('文件上传失败') this.$modal.msgError('文件上传失败')
}, },
/** 删除文件 */ /** 删除文件 */
handleRemove(file, fileList) { handleRemove(file, fileList) {
this.fileList = fileList this.fileList = fileList
this.updateFormFiles() this.updateFormFiles()
}, },
/** 文件数量超出限制 */ /** 文件数量超出限制 */
handleExceed(files, fileList) { handleExceed(files, fileList) {
// 由于已实现自动替换,此方法不再需要显示错误提示 // 由于已实现自动替换,此方法不再需要显示错误提示
// 新文件会自动替换旧文件 // 新文件会自动替换旧文件
}, },
/** 更新表单文件信息 */ /** 更新表单文件信息 */
updateFormFiles() { updateFormFiles() {
if (this.fileList && this.fileList.length > 0) { if (this.fileList && this.fileList.length > 0) {
// 单文件上传,直接取第一个文件 // 单文件上传,直接取第一个文件
const file = this.fileList[0] const file = this.fileList[0]
this.form.wjmc = file.name this.form.wjmc = file.name
this.form.wjlj = file.response ? file.response.url : file.url this.form.wjlj = file.response ? file.response.url : file.url
} else { } else {
this.form.wjmc = null this.form.wjmc = null
this.form.wjlj = null this.form.wjlj = null
} }
}, },
/** 下载文件 */ /** 下载文件 */
handleDownload(row) { handleDownload(row) {
// 检查是否有文件链接 // 检查是否有文件链接
if (!row.wjlj) { if (!row.wjlj) {
this.$modal.msgWarning('该文件没有下载链接') this.$modal.msgWarning('该文件没有下载链接')
return return
} }
// 处理多个文件的情况(逗号分隔) // 处理多个文件的情况(逗号分隔)
const fileNames = row.wjmc ? String(row.wjmc).split(',').filter(Boolean) : [] const fileNames = row.wjmc ? String(row.wjmc).split(',').filter(Boolean) : []
const fileLinks = String(row.wjlj).split(',').filter(Boolean) const fileLinks = String(row.wjlj).split(',').filter(Boolean)
if (fileLinks.length === 0) { if (fileLinks.length === 0) {
this.$modal.msgWarning('该文件没有下载链接') this.$modal.msgWarning('该文件没有下载链接')
return return
} }
// 如果只有一个文件,直接下载 // 如果只有一个文件,直接下载
if (fileLinks.length === 1) { if (fileLinks.length === 1) {
this.downloadFile(fileLinks[0], fileNames[0] || '文件') this.downloadFile(fileLinks[0], fileNames[0] || '文件')
return return
} }
// 如果有多个文件,显示选择列表 // 如果有多个文件,显示选择列表
const fileOptions = fileLinks.map((link, index) => { const fileOptions = fileLinks.map((link, index) => {
return { return {
name: fileNames[index] || `文件${index + 1}`, name: fileNames[index] || `文件${index + 1}`,
link: link link: link
} }
}) })
// 使用MessageBox选择要下载的文件 // 使用MessageBox选择要下载的文件
const h = this.$createElement const h = this.$createElement
const fileListHtml = fileOptions.map((file, index) => { const fileListHtml = fileOptions.map((file, index) => {
return h('div', { return h('div', {
style: { style: {
padding: '8px 0', padding: '8px 0',
cursor: 'pointer', cursor: 'pointer',
color: '#409EFF' color: '#409EFF'
}, },
on: { on: {
click: () => { click: () => {
this.downloadFile(file.link, file.name) this.downloadFile(file.link, file.name)
this.$msgbox.close() this.$msgbox.close()
} }
} }
}, file.name) }, file.name)
}) })
this.$msgbox({ this.$msgbox({
title: '请选择要下载的文件', title: '请选择要下载的文件',
message: h('div', null, fileListHtml), message: h('div', null, fileListHtml),
showCancelButton: true, showCancelButton: true,
confirmButtonText: '取消', confirmButtonText: '取消',
cancelButtonText: '', cancelButtonText: '',
showConfirmButton: true, showConfirmButton: true,
showCancelButton: false showCancelButton: false
}).catch(() => {}) }).catch(() => {})
}, },
/** 执行文件下载 */ /** 执行文件下载 */
downloadFile(url, fileName) { downloadFile(url, fileName) {
if (!url) { if (!url) {
this.$modal.msgWarning('文件链接无效') this.$modal.msgWarning('文件链接无效')
return return
} }
// 创建一个a标签进行下载 // 创建一个a标签进行下载
const link = document.createElement('a') const link = document.createElement('a')
link.style.display = 'none' link.style.display = 'none'
link.href = url link.href = url
link.setAttribute('download', fileName) link.setAttribute('download', fileName)
link.setAttribute('target', '_blank') link.setAttribute('target', '_blank')
document.body.appendChild(link) document.body.appendChild(link)
link.click() link.click()
document.body.removeChild(link) document.body.removeChild(link)
this.$modal.msgSuccess('开始下载文件') this.$modal.msgSuccess('开始下载文件')
}, },
/** 查询项目成员列表 */ /** 查询项目成员列表 */
getXmcyList() { getXmcyList() {
if (!this.xmcyQueryParams.zbid) { if (!this.xmcyQueryParams.zbid) {
return return
} }
this.xmcyLoading = true this.xmcyLoading = true
listXmcy(this.xmcyQueryParams).then(response => { listXmcy(this.xmcyQueryParams).then(response => {
this.xmcyList = response.rows || [] this.xmcyList = response.rows || []
this.xmcyTotal = response.total || 0 this.xmcyTotal = response.total || 0
this.xmcyLoading = false this.xmcyLoading = false
}).catch(() => { }).catch(() => {
this.xmcyLoading = false this.xmcyLoading = false
}) })
}, },
/** 搜索项目成员 */ /** 搜索项目成员 */
handleXmcyQuery() { handleXmcyQuery() {
this.xmcyQueryParams.pageNum = 1 this.xmcyQueryParams.pageNum = 1
this.getXmcyList() this.getXmcyList()
}, },
/** 重置项目成员查询 */ /** 重置项目成员查询 */
resetXmcyQuery() { resetXmcyQuery() {
this.xmcyQueryParams = { this.xmcyQueryParams = {
pageNum: 1, pageNum: 1,
pageSize: 9999, pageSize: 9999,
nickName: null, nickName: null,
zbid: this.queryParams.xmid zbid: this.queryParams.xmid
} }
this.getXmcyList() this.getXmcyList()
}, },
/** 项目成员表单重置 */ /** 项目成员表单重置 */
resetXmcyForm() { resetXmcyForm() {
this.xmcyForm = { this.xmcyForm = {
cyid: null, cyid: null,
zbid: this.queryParams.xmid, zbid: this.queryParams.xmid,
userId: null, userId: null,
nickName: null, nickName: null,
xmjs: null, xmjs: null,
createdBy: null, createdBy: null,
createdTime: null, createdTime: null,
updateBy: null, updateBy: null,
updateTime: null updateTime: null
} }
this.resetForm("xmcyForm") this.resetForm("xmcyForm")
}, },
/** 新增项目成员按钮操作 */ /** 新增项目成员按钮操作 */
handleXmcyAdd() { handleXmcyAdd() {
this.resetXmcyForm() this.resetXmcyForm()
this.xmcyOpen = true this.xmcyOpen = true
this.xmcyTitle = "添加项目成员" this.xmcyTitle = "添加项目成员"
}, },
/** 修改项目成员按钮操作 */ /** 修改项目成员按钮操作 */
handleXmcyUpdate(row) { handleXmcyUpdate(row) {
this.resetXmcyForm() this.resetXmcyForm()
const cyid = row.cyid const cyid = row.cyid
getXmcy(cyid).then(response => { getXmcy(cyid).then(response => {
this.xmcyForm = response.data this.xmcyForm = response.data
// 确保zbid被设置 // 确保zbid被设置
this.xmcyForm.zbid = this.queryParams.xmid this.xmcyForm.zbid = this.queryParams.xmid
this.xmcyOpen = true this.xmcyOpen = true
this.xmcyTitle = "修改项目成员" this.xmcyTitle = "修改项目成员"
}) })
}, },
/** 提交项目成员表单 */ /** 提交项目成员表单 */
submitXmcyForm() { submitXmcyForm() {
this.$refs["xmcyForm"].validate(valid => { this.$refs["xmcyForm"].validate(valid => {
if (valid) { if (valid) {
if (this.xmcyForm.cyid != null) { if (this.xmcyForm.cyid != null) {
updateXmcy(this.xmcyForm).then(response => { updateXmcy(this.xmcyForm).then(response => {
this.$modal.msgSuccess("修改成功") this.$modal.msgSuccess("修改成功")
this.xmcyOpen = false this.xmcyOpen = false
this.getXmcyList() this.getXmcyList()
}) })
} else { } else {
addXmcy(this.xmcyForm).then(response => { addXmcy(this.xmcyForm).then(response => {
this.$modal.msgSuccess("新增成功") this.$modal.msgSuccess("新增成功")
this.xmcyOpen = false this.xmcyOpen = false
this.getXmcyList() this.getXmcyList()
}) })
} }
} }
}) })
}, },
/** 删除项目成员按钮操作 */ /** 删除项目成员按钮操作 */
handleXmcyDelete(row) { handleXmcyDelete(row) {
const cyid = row.cyid const cyid = row.cyid
delXmcy(cyid).then(() => { delXmcy(cyid).then(() => {
this.getXmcyList() this.getXmcyList()
this.$modal.msgSuccess("删除成功") this.$modal.msgSuccess("删除成功")
}).catch(() => {}) }).catch(() => {})
}, },
/** 取消项目成员表单 */ /** 取消项目成员表单 */
cancelXmcy() { cancelXmcy() {
this.xmcyOpen = false this.xmcyOpen = false
this.resetXmcyForm() this.resetXmcyForm()
}, },
/** 打开选择用户对话框 */ /** 打开选择用户对话框 */
openUserDialog() { openUserDialog() {
// 重置查询参数 // 重置查询参数
this.userQueryParams = { this.userQueryParams = {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
userName: null userName: null
} }
this.userList = [] this.userList = []
this.userTotal = 0 this.userTotal = 0
this.userDialogVisible = true this.userDialogVisible = true
this.getUserList() this.getUserList()
}, },
/** 搜索用户 */ /** 搜索用户 */
handleUserSearch() { handleUserSearch() {
this.userQueryParams.pageNum = 1 this.userQueryParams.pageNum = 1
this.getUserList() this.getUserList()
}, },
/** 查询用户列表 */ /** 查询用户列表 */
getUserList() { getUserList() {
this.userLoading = true this.userLoading = true
listUser(this.userQueryParams).then(response => { listUser(this.userQueryParams).then(response => {
this.userList = response.rows || [] this.userList = response.rows || []
this.userTotal = response.total || 0 this.userTotal = response.total || 0
this.userLoading = false this.userLoading = false
}).catch(() => { }).catch(() => {
this.userLoading = false this.userLoading = false
}) })
}, },
/** 重置用户查询 */ /** 重置用户查询 */
resetUserQuery() { resetUserQuery() {
this.userQueryParams = { this.userQueryParams = {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
userName: null userName: null
} }
this.userList = [] this.userList = []
this.userTotal = 0 this.userTotal = 0
}, },
/** 选择用户 */ /** 选择用户 */
handleUserSelect(row) { handleUserSelect(row) {
this.xmcyForm.userId = row.userId this.xmcyForm.userId = row.userId
this.xmcyForm.nickName = row.nickName this.xmcyForm.nickName = row.nickName
this.xmcyForm.phonenumber = row.phonenumber this.xmcyForm.phonenumber = row.phonenumber
this.userDialogVisible = false this.userDialogVisible = false
}, },
/** 切换搜索框显示 */ /** 切换搜索框显示 */
toggleSearch() { toggleSearch() {
this.showSearchBox = !this.showSearchBox this.showSearchBox = !this.showSearchBox
if (this.showSearchBox) { if (this.showSearchBox) {
this.$nextTick(() => { this.$nextTick(() => {
const searchInput = this.$el.querySelector('.xmcy-search input') const searchInput = this.$el.querySelector('.xmcy-search input')
if (searchInput) { if (searchInput) {
searchInput.focus() searchInput.focus()
} }
}) })
} else { } else {
// 隐藏搜索框时重置查询 // 隐藏搜索框时重置查询
this.resetXmcyQuery() this.resetXmcyQuery()
} }
}, },
/** 下拉菜单命令处理 */ /** 下拉菜单命令处理 */
handleCommand(command) { handleCommand(command) {
if (command.action === 'edit') { if (command.action === 'edit') {
this.handleXmcyUpdate(command.row) this.handleXmcyUpdate(command.row)
} }
}, },
/** 获取头像样式类 */ /** 获取头像样式类 */
getAvatarClass(item) { getAvatarClass(item) {
// 根据角色返回不同的样式类,管理员用绿色,其他用蓝色 // 根据角色返回不同的样式类,管理员用绿色,其他用蓝色
if (this.isOwner(item.xmjs)) { if (this.isOwner(item.xmjs)) {
return 'avatar-admin' return 'avatar-admin'
} }
return 'avatar-member' return 'avatar-member'
}, },
/** 获取头像图标 */ /** 获取头像图标 */
getAvatarIcon(item) { getAvatarIcon(item) {
if (this.isOwner(item.xmjs)) { if (this.isOwner(item.xmjs)) {
return 'el-icon-user-solid' return 'el-icon-user-solid'
} }
return 'el-icon-user' return 'el-icon-user'
}, },
/** 判断是否为所有者/管理员 */ /** 判断是否为所有者/管理员 */
isOwner(xmjs) { isOwner(xmjs) {
// 查找字典中标签包含"管理员"或"所有者"的角色 // 查找字典中标签包含"管理员"或"所有者"的角色
if (!this.dict || !this.dict.type || !this.dict.type.xm_js) { if (!this.dict || !this.dict.type || !this.dict.type.xm_js) {
return false return false
} }
const role = this.dict.type.xm_js.find(item => item.value === xmjs) const role = this.dict.type.xm_js.find(item => item.value === xmjs)
if (role && (role.label.includes('管理员') || role.label.includes('所有者'))) { if (role && (role.label.includes('管理员') || role.label.includes('所有者'))) {
return true return true
} }
// 如果字典值为'1'或'admin'等,也认为是管理员 // 如果字典值为'1'或'admin'等,也认为是管理员
return xmjs === '1' || xmjs === 'admin' return xmjs === '1' || xmjs === 'admin'
}, },
/** 判断是否为查看者角色 */ /** 判断是否为查看者角色 */
checkViewerRole() { checkViewerRole() {
if (!this.currentUserRole || !this.dict || !this.dict.type || !this.dict.type.xm_js) { if (!this.currentUserRole || !this.dict || !this.dict.type || !this.dict.type.xm_js) {
this.isViewerRole = false this.isViewerRole = false
return return
} }
// 查找字典中标签包含"查看者"的角色 // 查找字典中标签包含"查看者"的角色
const role = this.dict.type.xm_js.find(item => item.value === this.currentUserRole) const role = this.dict.type.xm_js.find(item => item.value === this.currentUserRole)
if (role && role.label.includes('查看者')) { if (role && role.label.includes('查看者')) {
this.isViewerRole = true this.isViewerRole = true
} else { } else {
this.isViewerRole = false this.isViewerRole = false
} }
} }
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.main-layout { .main-layout {
display: flex; display: flex;
gap: 20px; gap: 20px;
height: calc(100vh - 150px); height: calc(100vh - 150px);
} }
.left-panel { .left-panel {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
// 查看者模式下,左侧面板占满整个宽度 // 查看者模式下,左侧面板占满整个宽度
.main-layout.viewer-mode .left-panel { .main-layout.viewer-mode .left-panel {
width: 100%; width: 100%;
} }
.right-panel { .right-panel {
width: 410px; width: 410px;
flex-shrink: 0; flex-shrink: 0;
border-left: 1px solid #e8eaed; border-left: 1px solid #e8eaed;
padding-left: 20px; padding-left: 20px;
padding-right: 20px; padding-right: 20px;
} }
.xmcy-panel { .xmcy-panel {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.xmcy-header { .xmcy-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 14px; margin-bottom: 14px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px solid #e8eaed; border-bottom: 1px solid #e8eaed;
.xmcy-title { .xmcy-title {
margin: 0; margin: 0;
font-size: 15px; font-size: 15px;
font-weight: 600; font-weight: 600;
color: #212121; color: #212121;
letter-spacing: 0.1px; letter-spacing: 0.1px;
} }
.xmcy-actions { .xmcy-actions {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
.invite-btn { .invite-btn {
height: 28px; height: 28px;
padding: 0 12px; padding: 0 12px;
font-size: 12px; font-size: 12px;
border-radius: 4px; border-radius: 4px;
font-weight: 500; font-weight: 500;
} }
.search-btn { .search-btn {
padding: 4px; padding: 4px;
font-size: 16px; font-size: 16px;
color: #757575; color: #757575;
border-radius: 4px; border-radius: 4px;
transition: all 0.2s; transition: all 0.2s;
&:hover { &:hover {
color: #212121; color: #212121;
background: rgba(0, 0, 0, 0.04); background: rgba(0, 0, 0, 0.04);
} }
} }
} }
} }
.xmcy-search { .xmcy-search {
margin-bottom: 12px; margin-bottom: 12px;
} }
.xmcy-list { .xmcy-list {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding-right: 6px; padding-right: 6px;
padding-top: 2px; padding-top: 2px;
border: 1px solid #e8eaed; border: 1px solid #e8eaed;
} }
.xmcy-item { .xmcy-item {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 10px 12px; padding: 10px 12px;
margin-bottom: 8px; margin-bottom: 8px;
background: #fafafa; background: #fafafa;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
transition: all 0.2s ease; transition: all 0.2s ease;
&:hover { &:hover {
background: #f5f5f5; background: #f5f5f5;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
} }
.xmcy-item-left { .xmcy-item-left {
display: flex; display: flex;
align-items: center; align-items: center;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
} }
.xmcy-avatar { .xmcy-avatar {
width: 32px; width: 32px;
height: 32px; height: 32px;
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-right: 10px; margin-right: 10px;
flex-shrink: 0; flex-shrink: 0;
background: #e3f2fd; background: #e3f2fd;
color: #1976d2; color: #1976d2;
i { i {
font-size: 18px; font-size: 18px;
} }
&.avatar-admin { &.avatar-admin {
background: #e3f2fd; background: #e3f2fd;
color: #1976d2; color: #1976d2;
} }
&.avatar-member { &.avatar-member {
background: #e3f2fd; background: #e3f2fd;
color: #1976d2; color: #1976d2;
} }
} }
.xmcy-info { .xmcy-info {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px; gap: 4px;
} }
.xmcy-role-tag { .xmcy-role-tag {
margin: 0; margin: 0;
line-height: 1.2; line-height: 1.2;
::v-deep .el-tag { ::v-deep .el-tag {
margin: 0; margin: 0;
border: none; border: none;
font-size: 10px; font-size: 10px;
padding: 0; padding: 0;
background: transparent; background: transparent;
color: #999; color: #999;
height: auto; height: auto;
line-height: 1.2; line-height: 1.2;
font-weight: 400; font-weight: 400;
} }
} }
.xmcy-name { .xmcy-name {
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
color: #424242; color: #424242;
margin: 0; margin: 0;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
line-height: 1.3; line-height: 1.3;
} }
.xmcy-item-right { .xmcy-item-right {
flex-shrink: 0; flex-shrink: 0;
margin-left: 8px; margin-left: 8px;
.el-dropdown-link { .el-dropdown-link {
cursor: pointer; cursor: pointer;
color: #bbb; color: #bbb;
font-size: 14px; font-size: 14px;
padding: 4px; padding: 4px;
transition: color 0.2s; transition: color 0.2s;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 4px; border-radius: 4px;
&:hover { &:hover {
color: #666; color: #666;
background: rgba(0, 0, 0, 0.04); background: rgba(0, 0, 0, 0.04);
} }
i { i {
font-size: 14px; font-size: 14px;
} }
} }
} }
} }
.xmcy-empty { .xmcy-empty {
text-align: center; text-align: center;
padding: 60px 20px; padding: 60px 20px;
color: #9aa0a6; color: #9aa0a6;
p { p {
margin: 0; margin: 0;
font-size: 14px; font-size: 14px;
} }
} }
.xmcy-pagination { .xmcy-pagination {
margin-top: 14px; margin-top: 14px;
padding-top: 12px; padding-top: 12px;
border-top: 1px solid #e8eaed; border-top: 1px solid #e8eaed;
} }
// 滚动条样式 // 滚动条样式
.xmcy-list::-webkit-scrollbar { .xmcy-list::-webkit-scrollbar {
width: 6px; width: 6px;
} }
.xmcy-list::-webkit-scrollbar-track { .xmcy-list::-webkit-scrollbar-track {
background: #f1f1f1; background: #f1f1f1;
border-radius: 3px; border-radius: 3px;
} }
.xmcy-list::-webkit-scrollbar-thumb { .xmcy-list::-webkit-scrollbar-thumb {
background: #c1c1c1; background: #c1c1c1;
border-radius: 3px; border-radius: 3px;
&:hover { &:hover {
background: #a8a8a8; background: #a8a8a8;
} }
} }
</style> </style>
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
<!-- </el-select>--> <!-- </el-select>-->
<!-- </div>--> <!-- </div>-->
<toolbar-controls :plots="plots" :data-loaded="isWidgetReady" :show-line-style-panel="showLineStylePanel" <toolbar-controls :plots="plots" :data-loaded="isWidgetReady" :show-line-style-panel="showLineStylePanel"
@toggleLineStylePanel="toggleLineStylePanel" @exporting="exporting = $event" @toggleLineStylePanel="toggleLineStylePanel" @exporting="exporting = $event"
@closePrintDialog="showPrintDialog = false" /> @closePrintDialog="showPrintDialog = false" />
<el-tooltip content="设置" placement="bottom" effect="light"> <el-tooltip content="设置" placement="bottom" effect="light">
<el-button @click="editProperties" :class="{ 'active': showLineStylePanel }"> <el-button @click="editProperties" :class="{ 'active': showLineStylePanel }">
...@@ -55,11 +55,11 @@ ...@@ -55,11 +55,11 @@
</div> </div>
<!-- 缩略图 start--> <!-- 缩略图 start-->
<div v-if="shouldShowThumb" ref="thumbContainer" @click="handleThumbClick" @mousedown="startDrag" <div v-if="shouldShowThumb" ref="thumbContainer" @click="handleThumbClick" @mousedown="startDrag"
class="right-thumb draggable-thumb" :style="thumbPosition"> class="right-thumb draggable-thumb" :style="thumbPosition">
<!-- <div class="thumb-title">缩略图</div> --> <!-- <div class="thumb-title">缩略图</div> -->
<div ref="thumbHolder" class="thumb-holder"> <div ref="thumbHolder" class="thumb-holder">
<YsgcIndex v-if="thumbReady" :key="'thumb-' + routeId + '-' + thumbReady" :compact="true" :width="260" <YsgcIndex v-if="thumbReady" :key="'thumb-' + routeId + '-' + thumbReady" :compact="true" :width="260"
:height="160" :id-override="routeId" :active-segy-index="currentSegyIndex" /> :height="160" :id-override="routeId" :active-segy-index="currentSegyIndex" />
</div> </div>
<!-- 拖动指示器 --> <!-- 拖动指示器 -->
<div class="drag-handle"> <div class="drag-handle">
...@@ -68,17 +68,15 @@ ...@@ -68,17 +68,15 @@
</div> </div>
<!-- 缩略图 end--> <!-- 缩略图 end-->
<!-- 缩略图弹窗 start --> <!-- 缩略图弹窗 start -->
<!-- 缩略图弹窗 start -->
<el-dialog v-if="shouldShowThumb" title="缩略图预览" :visible.sync="showThumbDialog" width="80%" append-to-body <el-dialog v-if="shouldShowThumb" title="缩略图预览" :visible.sync="showThumbDialog" width="80%" append-to-body
:z-index="9999999" :modal="true" :close-on-click-modal="true" :close-on-press-escape="true" :z-index="9999999" :modal="true" :close-on-click-modal="true" :close-on-press-escape="true"
custom-class="thumbnail-dialog"> custom-class="thumbnail-dialog">
<div style="height: 100%;"> <div style="height: 100%;">
<YsgcIndex :key="'dialog-thumb-' + routeId" :id-override="routeId" :active-segy-index="currentSegyIndex" <YsgcIndex :key="'dialog-thumb-' + routeId" :id-override="routeId" :active-segy-index="currentSegyIndex"
@segyLinePick="onThumbLinePick" /> @segyLinePick="onThumbLinePick" />
</div> </div>
</el-dialog> </el-dialog>
<!-- 缩略图弹窗 end --> <!-- 缩略图弹窗 end -->
<!-- 缩略图弹窗 end -->
</div> </div>
<!-- 添加加载状态和错误提示 --> <!-- 添加加载状态和错误提示 -->
...@@ -90,16 +88,16 @@ ...@@ -90,16 +88,16 @@
</div> </div>
</div> </div>
<div class="split-container" :class="layoutMode === 'vertical' ? 'layout-vertical' : 'layout-horizontal'" <div class="split-container" :class="layoutMode === 'vertical' ? 'layout-vertical' : 'layout-horizontal'"
v-loading="isLoading" element-loading-text="加载中..."> v-loading="isLoading" element-loading-text="加载中...">
<div class="sync-section" ref="topScroll" @scroll="onScroll('top')"> <div class="sync-section" ref="topScroll" @scroll="onScroll('top')">
<div class="zoom-wrapper" ref="topWrapper" <div class="zoom-wrapper" ref="topWrapper"
:style="{ width: baseCanvasWidth + 'px', height: baseCanvasHeight + 'px' }"> :style="{ width: baseCanvasWidth + 'px', height: baseCanvasHeight + 'px' }">
<canvas ref="plot" id="canvasTop" class="plot-canvas" /> <canvas ref="plot" id="canvasTop" class="plot-canvas" />
</div> </div>
</div> </div>
<div class="sync-section" ref="bottomScroll" @scroll="onScroll('bottom')"> <div class="sync-section" ref="bottomScroll" @scroll="onScroll('bottom')">
<div class="zoom-wrapper" ref="bottomWrapper" <div class="zoom-wrapper" ref="bottomWrapper"
:style="{ width: baseCanvasWidth + 'px', height: baseCanvasHeight + 'px' }"> :style="{ width: baseCanvasWidth + 'px', height: baseCanvasHeight + 'px' }">
<canvas ref="plot2" id="canvasBottom" class="plot-canvas" /> <canvas ref="plot2" id="canvasBottom" class="plot-canvas" />
</div> </div>
</div> </div>
...@@ -113,7 +111,7 @@ ...@@ -113,7 +111,7 @@
<div v-if="item.type === 'separator'" class="context-menu-separator"> <div v-if="item.type === 'separator'" class="context-menu-separator">
</div> </div>
<div v-else class="context-menu-item" :class="{ disabled: !isMenuItemEnabled(item.action) }" <div v-else class="context-menu-item" :class="{ disabled: !isMenuItemEnabled(item.action) }"
@click="handleMenuAction(item.action)"> @click="handleMenuAction(item.action)">
<span> <span>
<i :class="getElementIcon(item.icon)" class="mr-2"></i> <i :class="getElementIcon(item.icon)" class="mr-2"></i>
{{ item.text }} {{ item.text }}
...@@ -182,7 +180,7 @@ ...@@ -182,7 +180,7 @@
<span class="style-label">字体:</span> <span class="style-label">字体:</span>
<el-select v-model="textStyle.font" placeholder="选择字体" @change="handleFontChange"> <el-select v-model="textStyle.font" placeholder="选择字体" @change="handleFontChange">
<el-option v-for="(font, index) in Object.keys(TEXT_PATTERNS)" :key="index" :label="font" :value="font" <el-option v-for="(font, index) in Object.keys(TEXT_PATTERNS)" :key="index" :label="font" :value="font"
:style="{ fontFamily: font }"> :style="{ fontFamily: font }">
{{ font }} {{ font }}
</el-option> </el-option>
</el-select> </el-select>
...@@ -192,25 +190,25 @@ ...@@ -192,25 +190,25 @@
<div class="text-style-buttons"> <div class="text-style-buttons">
<el-button-group style="width: 100%"> <el-button-group style="width: 100%">
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.align === 'left' ? 'primary' : ''" <el-button style="min-width: 20%; max-width: 20%" :type="textStyle.align === 'left' ? 'primary' : ''"
@click="textStyle.align = 'left'; updateTextStyle()" title="左对齐"> @click="textStyle.align = 'left'; updateTextStyle()" title="左对齐">
<i class="el-icon-s-fold"></i> <i class="el-icon-s-fold"></i>
</el-button> </el-button>
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.align === 'center' ? 'primary' : ''" <el-button style="min-width: 20%; max-width: 20%" :type="textStyle.align === 'center' ? 'primary' : ''"
@click="textStyle.align = 'center'; updateTextStyle()" title="居中对齐"> @click="textStyle.align = 'center'; updateTextStyle()" title="居中对齐">
<i class="el-icon-menu"></i> <i class="el-icon-menu"></i>
</el-button> </el-button>
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.align === 'right' ? 'primary' : ''" <el-button style="min-width: 20%; max-width: 20%" :type="textStyle.align === 'right' ? 'primary' : ''"
@click="textStyle.align = 'right'; updateTextStyle()" title="右对齐"> @click="textStyle.align = 'right'; updateTextStyle()" title="右对齐">
<i class="el-icon-s-unfold"></i> <i class="el-icon-s-unfold"></i>
</el-button> </el-button>
<!-- 粗体按钮 --> <!-- 粗体按钮 -->
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.bold ? 'primary' : ''" <el-button style="min-width: 20%; max-width: 20%" :type="textStyle.bold ? 'primary' : ''"
@click="textStyle.bold = !textStyle.bold; updateTextStyle()" title="粗体"> @click="textStyle.bold = !textStyle.bold; updateTextStyle()" title="粗体">
<span style="font-weight: bold">B</span> <span style="font-weight: bold">B</span>
</el-button> </el-button>
<!-- 斜体按钮 --> <!-- 斜体按钮 -->
<el-button style="min-width: 20%; max-width: 20%" :type="textStyle.italic ? 'primary' : ''" <el-button style="min-width: 20%; max-width: 20%" :type="textStyle.italic ? 'primary' : ''"
@click="textStyle.italic = !textStyle.italic; updateTextStyle()" title="斜体"> @click="textStyle.italic = !textStyle.italic; updateTextStyle()" title="斜体">
<span style="font-style: italic">I</span> <span style="font-style: italic">I</span>
</el-button> </el-button>
</el-button-group> </el-button-group>
...@@ -222,7 +220,7 @@ ...@@ -222,7 +220,7 @@
<div class="style-item"> <div class="style-item">
<span class="style-label">边框圆角:</span> <span class="style-label">边框圆角:</span>
<el-input-number v-model="textStyle.borderRadius" :min="0" :max="30" size="small" <el-input-number v-model="textStyle.borderRadius" :min="0" :max="30" size="small"
@change="handleBorderRadiusChange"> @change="handleBorderRadiusChange">
</el-input-number> </el-input-number>
</div> </div>
<div class="style-item"> <div class="style-item">
...@@ -284,7 +282,7 @@ ...@@ -284,7 +282,7 @@
<el-col :span="5"> <el-col :span="5">
<el-form-item label="归一化比例"> <el-form-item label="归一化比例">
<el-slider @change="changeNor" style="width: 180px;" v-model="NormalizationBl" :min="0.1" :max="5" <el-slider @change="changeNor" style="width: 180px;" v-model="NormalizationBl" :min="0.1" :max="5"
:step="0.1" show-stops> :step="0.1" show-stops>
</el-slider> </el-slider>
</el-form-item> </el-form-item>
</el-col> </el-col>
...@@ -813,6 +811,14 @@ export default { ...@@ -813,6 +811,14 @@ export default {
// height: 0.1452145214521452 // height: 0.1452145214521452
// } // }
// }], // }],
// 添加鼠标悬停相关的数据
cursorInfo: {
depth: null,
trace: null,
value: null
},
showCursorInfo: true,
isHovering: false,
// 添加画笔工具相关属性 // 添加画笔工具相关属性
pencilTool: null, pencilTool: null,
selectedShape: null, selectedShape: null,
...@@ -881,6 +887,9 @@ export default { ...@@ -881,6 +887,9 @@ export default {
'DashDot': [20, 4, 2, 4], 'DashDot': [20, 4, 2, 4],
'DashDotDot': [20, 4, 2, 4, 2, 4] 'DashDotDot': [20, 4, 2, 4, 2, 4]
}, },
isWidgetReady: false,
loadingError: null,
pipeline: null,
colorProvider: null, colorProvider: null,
// 右键菜单相关数据 // 右键菜单相关数据
contextMenu: { contextMenu: {
...@@ -1154,7 +1163,7 @@ export default { ...@@ -1154,7 +1163,7 @@ export default {
}, },
// 监听routeId变化 // 监听routeId变化
routeId(newId, oldId) { routeId(newId, oldId) {
// console.log('[index2] routeId变化:', newId, oldId); //console.log('[index2] routeId变化:', newId, oldId);
if (newId !== oldId) { if (newId !== oldId) {
this.handleRouteChange(); this.handleRouteChange();
// 强制刷新缩略图 // 强制刷新缩略图
...@@ -1162,69 +1171,66 @@ export default { ...@@ -1162,69 +1171,66 @@ export default {
} }
} }
}, },
// mounted() {
//
// console.log(1111)
// // 初始化时强制设置为初始状态
// this.resetToInitialState();
//
// // 立即禁用滚动条工具
// this.disableScrollbarTools();
//
// // 添加强制隐藏滚动条的CSS
// this.addForceHideScrollbarCSS();
//
// // 关闭强力滚动条监控,避免影响全局布局
// // this.startAggressiveScrollbarMonitor();
//
// this.$nextTick(async () => {
// ['thumb-test-container', 'thumb-chart-container', 'super-visible-thumb'].forEach(id => {
// const el = document.getElementById(id);
// if (el) try { el.remove(); } catch (e) { }
// });
// // 保持缩略图容器在组件内部,避免在DOM树中难以定位
// // 初始化场景时不自动加载演示数据,等待接口结果决定
// this.shouldLoadDemo = false;
// this.createScene(this.$refs.plot, { autoloadDemo: false });
// // 等DOM稳定后再渲染缩略图组件
// setTimeout(() => {
// this.thumbReady = true;
// // 再兜底:确保右上角出现内容,并直接绘制缩略图
// setTimeout(() => {
// this.ensureThumbRendered();
// this.drawThumbDirect();
// }, 200);
// }, 100);
// // 已改为内嵌 YsgcIndex 缩略图,不再走旧的 ECharts 缩略图初始化
//
// // 自动拉取接口数据并加载第一组上/下部 SEGY(jbsegy -> 顶部,xbsegy -> 底部)
// try {
// await this.loadThumbData();
// if (Array.isArray(this.segyList) && this.segyList.length > 0) {
// await this.loadCurrentSegy();
// } else {
// // 无接口数据:若没有路由id,允许加载演示数据;若有id但无数据,保持空白
// const hasId = !!this.routeId;
// if (!hasId) {
// this.shouldLoadDemo = true;
// try { await this.handleFileSelect(this.plots); } catch (e) { }
// } else {
// this.clearCurrentPlots();
// console.info('[index2] 该项目无可用SEGY数据');
// }
// }
// } catch (e) {
// console.warn('[index2] 自动加载数据失败:', e);
// // 加载失败时,如果没有路由ID,尝试加载演示数据
// if (!this.routeId) {
// this.shouldLoadDemo = true;
// try { await this.handleFileSelect(this.plots); } catch (e) { }
// }
// }
// });
// },
mounted() { mounted() {
console.log(2222) // 初始化时强制设置为初始状态
this.resetToInitialState();
// 立即禁用滚动条工具
this.disableScrollbarTools();
// 添加强制隐藏滚动条的CSS
this.addForceHideScrollbarCSS();
// 关闭强力滚动条监控,避免影响全局布局
// this.startAggressiveScrollbarMonitor();
this.$nextTick(async () => {
['thumb-test-container', 'thumb-chart-container', 'super-visible-thumb'].forEach(id => {
const el = document.getElementById(id);
if (el) try { el.remove(); } catch (e) { }
});
// 保持缩略图容器在组件内部,避免在DOM树中难以定位
// 初始化场景时不自动加载演示数据,等待接口结果决定
this.shouldLoadDemo = false;
this.createScene(this.$refs.plot, { autoloadDemo: false });
// 等DOM稳定后再渲染缩略图组件
setTimeout(() => {
this.thumbReady = true;
// 再兜底:确保右上角出现内容,并直接绘制缩略图
setTimeout(() => {
this.ensureThumbRendered();
this.drawThumbDirect();
}, 200);
}, 100);
// 已改为内嵌 YsgcIndex 缩略图,不再走旧的 ECharts 缩略图初始化
// 自动拉取接口数据并加载第一组上/下部 SEGY(jbsegy -> 顶部,xbsegy -> 底部)
try {
await this.loadThumbData();
if (Array.isArray(this.segyList) && this.segyList.length > 0) {
await this.loadCurrentSegy();
} else {
// 无接口数据:若没有路由id,允许加载演示数据;若有id但无数据,保持空白
const hasId = !!this.routeId;
if (!hasId) {
this.shouldLoadDemo = true;
try { await this.handleFileSelect(this.plots); } catch (e) { }
} else {
this.clearCurrentPlots();
console.info('[index2] 该项目无可用SEGY数据');
}
}
} catch (e) {
console.warn('[index2] 自动加载数据失败:', e);
// 加载失败时,如果没有路由ID,尝试加载演示数据
if (!this.routeId) {
this.shouldLoadDemo = true;
try { await this.handleFileSelect(this.plots); } catch (e) { }
}
}
});
},
mounted() {
// 将缩略图容器移动到body,确保fixed基于视口 // 将缩略图容器移动到body,确保fixed基于视口
if (this.$refs.thumbContainer && this.$refs.thumbContainer.parentNode !== document.body) { if (this.$refs.thumbContainer && this.$refs.thumbContainer.parentNode !== document.body) {
document.body.appendChild(this.$refs.thumbContainer); document.body.appendChild(this.$refs.thumbContainer);
...@@ -2180,8 +2186,6 @@ export default { ...@@ -2180,8 +2186,6 @@ export default {
//console.log('[index2] 加载segy文件 Top/Bottom:', (currentSegy.jbsegyName || currentSegy.jbsegy), (currentSegy.xbsegyName || currentSegy.xbsegy)); //console.log('[index2] 加载segy文件 Top/Bottom:', (currentSegy.jbsegyName || currentSegy.jbsegy), (currentSegy.xbsegyName || currentSegy.xbsegy));
//console.log('[index2] segy文件路径 Top/Bottom:', currentSegy.jbsegy, currentSegy.xbsegy); //console.log('[index2] segy文件路径 Top/Bottom:', currentSegy.jbsegy, currentSegy.xbsegy);
//
try { try {
// 双图加载:jbsegy -> 顶部;xbsegy -> 底部 // 双图加载:jbsegy -> 顶部;xbsegy -> 底部
// 根据后台返回的 ysqqXmxxSegy 数据,jbsegy 顶部展示,xbsegy 底部展示 // 根据后台返回的 ysqqXmxxSegy 数据,jbsegy 顶部展示,xbsegy 底部展示
...@@ -2205,9 +2209,6 @@ export default { ...@@ -2205,9 +2209,6 @@ export default {
try { this._seismicWidget && this._seismicWidget.invalidate && this._seismicWidget.invalidate(); } catch (e) { } try { this._seismicWidget && this._seismicWidget.invalidate && this._seismicWidget.invalidate(); } catch (e) { }
// 关键修复:切换下一道后,强制将底部 widget 绑定为当前 pipeline,并重新插入绘制工具,防止短暂显示上一道 // 关键修复:切换下一道后,强制将底部 widget 绑定为当前 pipeline,并重新插入绘制工具,防止短暂显示上一道
try { try {
// 确保 pipeline/路径键为当前
// await this.assertBottomPipelineCurrent();
this.refreshBottomDisplay();
// 确保 pipeline/路径键为当前(只在必要时调用,避免频繁刷新) // 确保 pipeline/路径键为当前(只在必要时调用,避免频繁刷新)
await this.assertBottomPipelineCurrent(); await this.assertBottomPipelineCurrent();
// 移除重复的 refreshBottomDisplay 调用,避免闪烁 // 移除重复的 refreshBottomDisplay 调用,避免闪烁
...@@ -2467,7 +2468,7 @@ export default { ...@@ -2467,7 +2468,7 @@ export default {
} }
console.log('[renderAnnotationsFromXbbznr] 开始解析标注数据,数量:', annotationList.length); console.log('[renderAnnotationsFromXbbznr] 开始解析标注数据,数量:', annotationList.length);
// console.log('[renderAnnotationsFromXbbznr] annotationList 详细内容:', JSON.stringify(annotationList, null, 2)); console.log('[renderAnnotationsFromXbbznr] annotationList 详细内容:', JSON.stringify(annotationList, null, 2));
// 清空现有的底部 savedAnnotations 和 savedLineAnnotations // 清空现有的底部 savedAnnotations 和 savedLineAnnotations
this.savedAnnotationsBottom = []; this.savedAnnotationsBottom = [];
...@@ -2878,143 +2879,116 @@ export default { ...@@ -2878,143 +2879,116 @@ export default {
this.isHovering = false; this.isHovering = false;
// 打开双图布局 // 打开双图布局
// this.showDualChart(); this.showDualChart();
// 不再隐藏容器,避免首次绘制依赖鼠标事件触发 // 不再隐藏容器,避免首次绘制依赖鼠标事件触发
const topScroll = this.$refs.topScroll; const topScroll = this.$refs.topScroll;
const bottomScroll = this.$refs.bottomScroll; const bottomScroll = this.$refs.bottomScroll;
// if (this.annotationTool){
// // if (this.annotationTool.isEnabled() && this.annotationTool.getMode() === PaintMode.Edit && this.annotationTool.getShape() != null) {
// if (Array.isArray(this.annotationTool.getShape())) {
// this.annotationTool.getShape().forEach((shape) => shape.dispose());
// } else {
// this.annotationTool.getShape().dispose();
// }
// this.annotationTool.editNode(null);
// // }
// }
var theCanvas = document.getElementById("canvasTop");
var w = theCanvas.width;
var h = theCanvas.height;
var context = theCanvas.getContext("2d");
context.clearRect(0, 0, w, h);
if(this.plots){
this.plots.dispose();
}
if(this.plotsBottom){
this.plotsBottom.dispose();
}
// 清空现有图表 // 清空现有图表
this.clearCurrentPlots(); this.clearCurrentPlots();
//
// 清空 pipeline 引用,确保旧数据被清除 // 清空 pipeline 引用,确保旧数据被清除
this.pipeline = null; this.pipeline = null;
this.pipelineBottom = null; this.pipelineBottom = null;
this.plots = null; this.plots = null;
this.plotsBottom = null; this.plotsBottom = null;
// // 确保两个画布已初始化(如果widget不存在,才创建新的)
// if (this.$refs.plot && !this._seismicWidget) { // 确保两个画布已初始化(如果widget不存在,才创建新的)
// this.createScene(this.$refs.plot, { autoloadDemo: false }); if (this.$refs.plot && !this._seismicWidget) {
// } this.createScene(this.$refs.plot, { autoloadDemo: false });
// if (this.$refs.plot2 && !this._seismicWidgetBottom) { }
// this.initSecondWidget(this.$refs.plot2); if (this.$refs.plot2 && !this._seismicWidgetBottom) {
// } this.initSecondWidget(this.$refs.plot2);
// // 重新绑定事件监听器 }
// this.$nextTick(() => { // 重新绑定事件监听器
// this.bindContextMenuListeners(); this.$nextTick(() => {
// }); this.bindContextMenuListeners();
// });
// // 等待一小段时间,确保清空操作完成
// await new Promise(resolve => setTimeout(resolve, 50)); // 等待一小段时间,确保清空操作完成
// await new Promise(resolve => setTimeout(resolve, 50));
// // 顺序加载,分别统计成功与失败,但不立即刷新
// let topOk = false; // 顺序加载,分别统计成功与失败,但不立即刷新
// let bottomOk = false; let topOk = false;
// try { let bottomOk = false;
// if (jbPath) { try {
// const topTitle = jbName || this.extractFileName(jbPath) || 'jbsegy'; if (jbPath) {
// await this.loadSegyIntoWidget('top', jbPath, topTitle, false); // 不立即刷新 const topTitle = jbName || this.extractFileName(jbPath) || 'jbsegy';
// topOk = true; await this.loadSegyIntoWidget('top', jbPath, topTitle, false); // 不立即刷新
// } topOk = true;
// } catch (e) { }
// console.warn('加载上部SEGY失败:', e); } catch (e) {
// } console.warn('加载上部SEGY失败:', e);
// }
// try {
// if (xbPath) { try {
// const bottomTitle = xbName || this.extractFileName(xbPath) || 'xbsegy'; if (xbPath) {
// await this.loadSegyIntoWidget('bottom', xbPath, bottomTitle, false); // 不立即刷新 const bottomTitle = xbName || this.extractFileName(xbPath) || 'xbsegy';
// bottomOk = true; await this.loadSegyIntoWidget('bottom', xbPath, bottomTitle, false); // 不立即刷新
// } bottomOk = true;
// } catch (e) { }
// console.warn('加载下部SEGY失败:', e); } catch (e) {
// } console.warn('加载下部SEGY失败:', e);
// }
// // 等待所有数据加载完成后再统一刷新
// if (topOk || bottomOk) { // 等待所有数据加载完成后再统一刷新
// // 使用 requestAnimationFrame 确保在下一帧统一刷新 if (topOk || bottomOk) {
// await this.$nextTick(); // 使用 requestAnimationFrame 确保在下一帧统一刷新
// requestAnimationFrame(() => { await this.$nextTick();
// try { requestAnimationFrame(() => {
// if (topOk && this._seismicWidget) { try {
// if (this._seismicWidget.invalidate) { if (topOk && this._seismicWidget) {
// this._seismicWidget.invalidate(); if (this._seismicWidget.invalidate) {
// } this._seismicWidget.invalidate();
// this.setScrollbarCSS(this._seismicWidget); }
// this.forceDisableInternalScrollbars(); this.setScrollbarCSS(this._seismicWidget);
// setTimeout(() => { this.forceDisableInternalScrollbars();
// try { setTimeout(() => {
// if (this._seismicWidget && this._seismicWidget.fitToBounds) { try {
// this._seismicWidget.fitToBounds(); if (this._seismicWidget && this._seismicWidget.fitToBounds) {
// } this._seismicWidget.fitToBounds();
// } catch (e) { } }
// }, 100); } catch (e) { }
// } }, 100);
// if (bottomOk && this._seismicWidgetBottom) { }
// if (this._seismicWidgetBottom.invalidate) { if (bottomOk && this._seismicWidgetBottom) {
// this._seismicWidgetBottom.invalidate(); if (this._seismicWidgetBottom.invalidate) {
// } this._seismicWidgetBottom.invalidate();
// this.setScrollbarCSS(this._seismicWidgetBottom); }
// this.forceDisableInternalScrollbars(); this.setScrollbarCSS(this._seismicWidgetBottom);
// setTimeout(() => { this.forceDisableInternalScrollbars();
// try { setTimeout(() => {
// if (this._seismicWidgetBottom && this._seismicWidgetBottom.fitToBounds) { try {
// this._seismicWidgetBottom.fitToBounds(); if (this._seismicWidgetBottom && this._seismicWidgetBottom.fitToBounds) {
// } this._seismicWidgetBottom.fitToBounds();
// // 额外在显示前进行一次尺寸同步与重绘 }
// try { this.updateBaseCanvasSize(); } catch (e) { } // 额外在显示前进行一次尺寸同步与重绘
// try { this.plotsBottom && typeof this.plotsBottom.redraw === 'function' && this.plotsBottom.redraw(); } catch (e) { } try { this.updateBaseCanvasSize(); } catch (e) { }
// } catch (e) { } try { this.plotsBottom && typeof this.plotsBottom.redraw === 'function' && this.plotsBottom.redraw(); } catch (e) { }
// }, 100); } catch (e) { }
// } }, 100);
// } catch (e) { }
// console.warn('最终刷新widget时出错:', e); } catch (e) {
// } console.warn('最终刷新widget时出错:', e);
// }); }
// });
// // 刷新完成后,额外进行一次尺寸同步与重绘,确保无悬浮也正确
// setTimeout(() => { // 刷新完成后,额外进行一次尺寸同步与重绘,确保无悬浮也正确
// try { this.updateBaseCanvasSize(); } catch (e) { } setTimeout(() => {
// try { this._seismicWidget && this._seismicWidget.invalidate && this._seismicWidget.invalidate(); } catch (e) { } try { this.updateBaseCanvasSize(); } catch (e) { }
// try { this._seismicWidgetBottom && this._seismicWidgetBottom.invalidate && this._seismicWidgetBottom.invalidate(); } catch (e) { } try { this._seismicWidget && this._seismicWidget.invalidate && this._seismicWidget.invalidate(); } catch (e) { }
// requestAnimationFrame(() => { try { this._seismicWidgetBottom && this._seismicWidgetBottom.invalidate && this._seismicWidgetBottom.invalidate(); } catch (e) { }
// try { this._seismicWidget && this._seismicWidget.invalidate && this._seismicWidget.invalidate(); } catch (e) { } requestAnimationFrame(() => {
// try { this._seismicWidgetBottom && this._seismicWidgetBottom.invalidate && this._seismicWidgetBottom.invalidate(); } catch (e) { } try { this._seismicWidget && this._seismicWidget.invalidate && this._seismicWidget.invalidate(); } catch (e) { }
// }); try { this._seismicWidgetBottom && this._seismicWidgetBottom.invalidate && this._seismicWidgetBottom.invalidate(); } catch (e) { }
// }, 200); });
// } else { }, 200);
// // 加载失败也做一次重绘尝试 } else {
// try { this._seismicWidget && this._seismicWidget.invalidate && this._seismicWidget.invalidate(); } catch (e) { } // 加载失败也做一次重绘尝试
// try { this._seismicWidgetBottom && this._seismicWidgetBottom.invalidate && this._seismicWidgetBottom.invalidate(); } catch (e) { } try { this._seismicWidget && this._seismicWidget.invalidate && this._seismicWidget.invalidate(); } catch (e) { }
// } try { this._seismicWidgetBottom && this._seismicWidgetBottom.invalidate && this._seismicWidgetBottom.invalidate(); } catch (e) { }
}
}, },
// 将指定segy加载到顶部/底部widget // 将指定segy加载到顶部/底部widget
// skipRefresh: 如果为 true,则不立即刷新,等待统一刷新 // skipRefresh: 如果为 true,则不立即刷新,等待统一刷新
...@@ -3184,7 +3158,7 @@ export default { ...@@ -3184,7 +3158,7 @@ export default {
// 关键修复:如果 pipeline、路径和名称都没有变化,不需要重新设置,避免不必要的刷新 // 关键修复:如果 pipeline、路径和名称都没有变化,不需要重新设置,避免不必要的刷新
if (this._seismicWidgetBottom.getPipeline && this._seismicWidgetBottom.getPipeline() === pipeline && if (this._seismicWidgetBottom.getPipeline && this._seismicWidgetBottom.getPipeline() === pipeline &&
String(this._bottomSegyPath) === String(newPath) && String(this._bottomSegyKey) === String(newKey)) { String(this._bottomSegyPath) === String(newPath) && String(this._bottomSegyKey) === String(newKey)) {
// 完全一致,不需要做任何操作 // 完全一致,不需要做任何操作
return; return;
} }
...@@ -4681,11 +4655,11 @@ export default { ...@@ -4681,11 +4655,11 @@ export default {
// 检查节点是否是文本节点 // 检查节点是否是文本节点
const isTextNode = (selectedNode.text !== undefined && selectedNode.text !== null) || const isTextNode = (selectedNode.text !== undefined && selectedNode.text !== null) ||
(typeof selectedNode.getText === 'function'); (typeof selectedNode.getText === 'function');
// 检查节点是否是路径节点(线条) // 检查节点是否是路径节点(线条)
const isPathNode = selectedNode.getType && selectedNode.getType() === 'Path' || const isPathNode = selectedNode.getType && selectedNode.getType() === 'Path' ||
(selectedNode.getProperties && selectedNode.getProperties().linestyle); (selectedNode.getProperties && selectedNode.getProperties().linestyle);
if (isTextNode && this.annotationToolBottom) { if (isTextNode && this.annotationToolBottom) {
// 文本节点:使用 annotationToolBottom 编辑 // 文本节点:使用 annotationToolBottom 编辑
...@@ -6286,9 +6260,6 @@ export default { ...@@ -6286,9 +6260,6 @@ export default {
} }
}, },
async handleFileSelect(plot) { async handleFileSelect(plot) {
let segys = []; let segys = [];
...@@ -6411,7 +6382,7 @@ export default { ...@@ -6411,7 +6382,7 @@ export default {
}); });
// Set the pipeline first // Set the pipeline first
this.pipeline= pipeline; this.pipeline = pipeline;
this._seismicWidget.setPipeline(pipeline); this._seismicWidget.setPipeline(pipeline);
// Then set widget options // Then set widget options
...@@ -6517,11 +6488,11 @@ export default { ...@@ -6517,11 +6488,11 @@ export default {
}); });
}, },
// handleMouseMove(event) { handleMouseMove(event) {
// if (!this._seismicWidget || !this.isHovering) return; if (!this._seismicWidget || !this.isHovering) return;
//
// // 使用内置状态栏,不需要额外的处理逻辑 // 使用内置状态栏,不需要额外的处理逻辑
// }, },
onFileOpen(evt, plot, fileInfo) { onFileOpen(evt, plot, fileInfo) {
evt.stopPropagation(); evt.stopPropagation();
...@@ -6600,11 +6571,11 @@ export default { ...@@ -6600,11 +6571,11 @@ export default {
// 检查节点是否是文本节点(通过检查是否有 text 属性或 getText 方法) // 检查节点是否是文本节点(通过检查是否有 text 属性或 getText 方法)
const isTextNode = (selectedNode.text !== undefined && selectedNode.text !== null) || const isTextNode = (selectedNode.text !== undefined && selectedNode.text !== null) ||
(typeof selectedNode.getText === 'function'); (typeof selectedNode.getText === 'function');
// 检查节点是否是路径节点(线条) // 检查节点是否是路径节点(线条)
const isPathNode = selectedNode.getType && selectedNode.getType() === 'Path' || const isPathNode = selectedNode.getType && selectedNode.getType() === 'Path' ||
(selectedNode.getProperties && selectedNode.getProperties().linestyle); (selectedNode.getProperties && selectedNode.getProperties().linestyle);
if (isTextNode && this.annotationTool) { if (isTextNode && this.annotationTool) {
// 文本节点:使用 annotationTool 编辑 // 文本节点:使用 annotationTool 编辑
...@@ -6888,185 +6859,185 @@ export default { ...@@ -6888,185 +6859,185 @@ export default {
// 添加事件监听器来打印文本绘制数据 // 添加事件监听器来打印文本绘制数据
this.annotationTool.addListener(EditEvents.Start, (tool, command) => { this.annotationTool.addListener(EditEvents.Start, (tool, command) => {
const node = command.getNode(); const node = command.getNode();
const textStyle = node.getTextStyle ? node.getTextStyle() : null;
// console.log('开始绘制文本:', {
// tool: tool.getMode(),
// node: {
// type: node.getType ? node.getType() : 'unknown',
// properties: node.getProperties(),
// bounds: node.getBounds ? node.getBounds() : null
// },
// textStyle: {
// font: textStyle ? textStyle.getFont() : null,
// color: textStyle ? textStyle.getColor() : null,
// size: textStyle ? textStyle.size || this.textStyle.size : this.textStyle.size,
// alignment: textStyle ? textStyle.getAlignment() : null
// },
// currentToolConfig: {
// mode: tool.getMode(),
// editMode: tool.getEditMode(),
// isEnabled: tool.isEnabled()
// }
// });
});
this.annotationTool.addListener(EditEvents.Change, (tool, event) => {
const node = tool.getShape();
if (node) {
const textStyle = node.getTextStyle ? node.getTextStyle() : null; const textStyle = node.getTextStyle ? node.getTextStyle() : null;
// console.log('开始绘制文本:', { const previousState = node._previousState || {};
// tool: tool.getMode(), //console.log('文本绘制更新:', {
// node: { // previous: {
// type: node.getType ? node.getType() : 'unknown', // properties: previousState.properties || {},
// textStyle: previousState.textStyle || {}
// },
// current: {
// properties: node.getProperties(), // properties: node.getProperties(),
// bounds: node.getBounds ? node.getBounds() : null // bounds: node.getBounds ? node.getBounds() : null,
// textStyle: {
// font: textStyle ? textStyle.getFont() : null,
// color: textStyle ? textStyle.getColor() : null,
// size: textStyle ? textStyle.size || this.textStyle.size : this.textStyle.size,
// alignment: textStyle ? textStyle.getAlignment() : null
// }
// }, // },
// textStyle: { // changes: this.detectChanges(previousState, node)
// font: textStyle ? textStyle.getFont() : null,
// color: textStyle ? textStyle.getColor() : null,
// size: textStyle ? textStyle.size || this.textStyle.size : this.textStyle.size,
// alignment: textStyle ? textStyle.getAlignment() : null
// },
// currentToolConfig: {
// mode: tool.getMode(),
// editMode: tool.getEditMode(),
// isEnabled: tool.isEnabled()
// }
// }); // });
}); // 保存当前状态用于下次比较
node._previousState = {
this.annotationTool.addListener(EditEvents.Change, (tool, event) => { properties: { ...node.getProperties() },
const node = tool.getShape(); textStyle: textStyle ? {
if (node) { font: textStyle.getFont(),
const textStyle = node.getTextStyle ? node.getTextStyle() : null; color: textStyle.getColor(),
const previousState = node._previousState || {}; size: textStyle.size,
//console.log('文本绘制更新:', { alignment: textStyle.getAlignment()
// previous: { } : {}
// properties: previousState.properties || {}, };
// textStyle: previousState.textStyle || {} }
// }, });
// current: {
// properties: node.getProperties(),
// bounds: node.getBounds ? node.getBounds() : null,
// textStyle: {
// font: textStyle ? textStyle.getFont() : null,
// color: textStyle ? textStyle.getColor() : null,
// size: textStyle ? textStyle.size || this.textStyle.size : this.textStyle.size,
// alignment: textStyle ? textStyle.getAlignment() : null
// }
// },
// changes: this.detectChanges(previousState, node)
// });
// 保存当前状态用于下次比较
node._previousState = {
properties: { ...node.getProperties() },
textStyle: textStyle ? {
font: textStyle.getFont(),
color: textStyle.getColor(),
size: textStyle.size,
alignment: textStyle.getAlignment()
} : {}
};
}
});
this.annotationTool.addListener(EditEvents.End, (tool, node) => { this.annotationTool.addListener(EditEvents.End, (tool, node) => {
if (node) { if (node) {
// 修复:按优先级提取文本,即使前面的方法返回空也要继续尝试 // 修复:按优先级提取文本,即使前面的方法返回空也要继续尝试
let text = ''; let text = '';
// 方法1: 优先从 node.text 直接属性获取(最可靠)
if (node.text !== undefined && node.text !== null && String(node.text).trim() !== '') {
text = String(node.text);
console.log('[annotationTool][End] ✓ 从 node.text 获取文本:', text);
}
// 方法2: 如果 node.text 为空,尝试 getText() 方法 // 方法1: 优先从 node.text 直接属性获取(最可靠)
if (!text || text.trim() === '') { if (node.text !== undefined && node.text !== null && String(node.text).trim() !== '') {
try { text = String(node.text);
if (typeof node.getText === 'function') { console.log('[annotationTool][End] ✓ 从 node.text 获取文本:', text);
const methodText = node.getText(); }
if (methodText !== undefined && methodText !== null && String(methodText).trim() !== '') {
text = String(methodText); // 方法2: 如果 node.text 为空,尝试 getText() 方法
console.log('[annotationTool][End] ✓ 从 getText() 获取文本:', text); if (!text || text.trim() === '') {
} try {
if (typeof node.getText === 'function') {
const methodText = node.getText();
if (methodText !== undefined && methodText !== null && String(methodText).trim() !== '') {
text = String(methodText);
console.log('[annotationTool][End] ✓ 从 getText() 获取文本:', text);
} }
} catch (e) {
console.warn('[annotationTool][End] getText() 调用失败:', e);
} }
} catch (e) {
console.warn('[annotationTool][End] getText() 调用失败:', e);
} }
}
// 方法3: 如果还是为空,尝试从 properties 中获取 // 方法3: 如果还是为空,尝试从 properties 中获取
if (!text || text.trim() === '') { if (!text || text.trim() === '') {
try { try {
if (node.getProperties && typeof node.getProperties === 'function') { if (node.getProperties && typeof node.getProperties === 'function') {
const props = node.getProperties(); const props = node.getProperties();
if (props && props.text !== undefined && props.text !== null && String(props.text).trim() !== '') { if (props && props.text !== undefined && props.text !== null && String(props.text).trim() !== '') {
text = String(props.text); text = String(props.text);
console.log('[annotationTool][End] ✓ 从 properties.text 获取文本:', text); console.log('[annotationTool][End] ✓ 从 properties.text 获取文本:', text);
}
} }
} catch (e) {
console.warn('[annotationTool][End] getProperties() 调用失败:', e);
} }
} catch (e) {
console.warn('[annotationTool][End] getProperties() 调用失败:', e);
} }
}
// 如果文本为空,记录警告 // 如果文本为空,记录警告
if (!text || text.trim() === '') { if (!text || text.trim() === '') {
console.warn('[annotationTool][End] ⚠️ 警告:文本节点但 text 为空!', { console.warn('[annotationTool][End] ⚠️ 警告:文本节点但 text 为空!', {
node,
nodeText: node.text,
hasGetText: typeof node.getText === 'function',
getTextResult: typeof node.getText === 'function' ? node.getText() : 'N/A',
properties: node.getProperties ? node.getProperties() : null
});
}
const textStyle = node.getTextStyle ? node.getTextStyle() : null;
console.log('[annotationTool][End] 绘制文本节点', {
node, node,
text,
nodeText: node.text, nodeText: node.text,
properties: node.getProperties && node.getProperties(), hasGetText: typeof node.getText === 'function',
textStyle: textStyle ? { getTextResult: typeof node.getText === 'function' ? node.getText() : 'N/A',
font: textStyle.getFont && textStyle.getFont(), properties: node.getProperties ? node.getProperties() : null
color: textStyle.getColor && textStyle.getColor(),
size: textStyle.size,
alignment: textStyle.getAlignment && textStyle.getAlignment()
} : null
}); });
}
// 关键修复:如果获取到文本,立即缓存并同步到节点 const textStyle = node.getTextStyle ? node.getTextStyle() : null;
if (text && text.trim() !== '') { console.log('[annotationTool][End] 绘制文本节点', {
try { node,
// 1. 同步文本到 properties text,
const props = node.getProperties ? node.getProperties() : {}; nodeText: node.text,
if (!props.text || String(props.text).trim() === '') { properties: node.getProperties && node.getProperties(),
if (typeof node.setProperties === 'function') { textStyle: textStyle ? {
node.setProperties({ ...props, text: text }); font: textStyle.getFont && textStyle.getFont(),
console.log('[annotationTool][End] ✓ 已同步文本到 properties.text:', text); color: textStyle.getColor && textStyle.getColor(),
} size: textStyle.size,
alignment: textStyle.getAlignment && textStyle.getAlignment()
} : null
});
// 关键修复:如果获取到文本,立即缓存并同步到节点
if (text && text.trim() !== '') {
try {
// 1. 同步文本到 properties
const props = node.getProperties ? node.getProperties() : {};
if (!props.text || String(props.text).trim() === '') {
if (typeof node.setProperties === 'function') {
node.setProperties({ ...props, text: text });
console.log('[annotationTool][End] ✓ 已同步文本到 properties.text:', text);
} }
}
// 2. 立即缓存到 collectedAnnotations(使用节点引用作为标识) // 2. 立即缓存到 collectedAnnotations(使用节点引用作为标识)
try { try {
// 检查是否已存在相同节点的缓存 // 检查是否已存在相同节点的缓存
const existingIndex = this.collectedAnnotations.findIndex(item => item._nodeRef === node); const existingIndex = this.collectedAnnotations.findIndex(item => item._nodeRef === node);
const cacheItem = { const cacheItem = {
type: 'text', type: 'text',
text: text, text: text,
properties: node.getProperties ? node.getProperties() : {}, properties: node.getProperties ? node.getProperties() : {},
textStyle: textStyle ? { textStyle: textStyle ? {
font: textStyle.getFont && textStyle.getFont(), font: textStyle.getFont && textStyle.getFont(),
color: textStyle.getColor && textStyle.getColor(), color: textStyle.getColor && textStyle.getColor(),
size: textStyle.size, size: textStyle.size,
alignment: textStyle.getAlignment && textStyle.getAlignment() alignment: textStyle.getAlignment && textStyle.getAlignment()
} : null, } : null,
panel: 'top', panel: 'top',
_nodeRef: node // 保存节点引用用于后续匹配 _nodeRef: node // 保存节点引用用于后续匹配
}; };
if (existingIndex >= 0) { if (existingIndex >= 0) {
// 更新现有缓存 // 更新现有缓存
this.collectedAnnotations[existingIndex] = cacheItem; this.collectedAnnotations[existingIndex] = cacheItem;
console.log('[annotationTool][End] ✓ 已更新缓存项 #' + existingIndex, text); console.log('[annotationTool][End] ✓ 已更新缓存项 #' + existingIndex, text);
} else { } else {
// 添加新缓存 // 添加新缓存
this.collectedAnnotations.push(cacheItem); this.collectedAnnotations.push(cacheItem);
console.log('[annotationTool][End] ✓ 已缓存文本到 collectedAnnotations:', text, '总数:', this.collectedAnnotations.length); console.log('[annotationTool][End] ✓ 已缓存文本到 collectedAnnotations:', text, '总数:', this.collectedAnnotations.length);
}
} catch (e) {
console.warn('[annotationTool][End] 缓存失败:', e);
} }
} catch (e) { } catch (e) {
console.warn('[annotationTool][End] 同步文本到 properties 失败:', e); console.warn('[annotationTool][End] 缓存失败:', e);
} }
} catch (e) {
console.warn('[annotationTool][End] 同步文本到 properties 失败:', e);
} }
}
// 确保节点被添加到图层 // 确保节点被添加到图层
if (this.annotations.indexOfChild(node) === -1) { if (this.annotations.indexOfChild(node) === -1) {
this.annotations.addChild(node); this.annotations.addChild(node);
console.log('[annotationTool][End] ✓ 已添加节点到图层'); console.log('[annotationTool][End] ✓ 已添加节点到图层');
}
} }
this.requestRepaint(); }
}); this.requestRepaint();
});
this.annotationTool.setEnabled(true); this.annotationTool.setEnabled(true);
...@@ -7947,7 +7918,8 @@ export default { ...@@ -7947,7 +7918,8 @@ export default {
if (!this.checkWidgetStatus() || !this.isWidgetReady) return; if (!this.checkWidgetStatus() || !this.isWidgetReady) return;
const widget = this.plots.getRoot(); // 修复:如果 plots 为 null,直接使用 _seismicWidget
const widget = (this.plots && this.plots.getRoot) ? this.plots.getRoot() : this._seismicWidget;
if (!widget) return; if (!widget) return;
// 确保注释层存在 // 确保注释层存在
...@@ -8014,9 +7986,42 @@ export default { ...@@ -8014,9 +7986,42 @@ export default {
if (node && this.annotations.indexOfChild(node) === -1) { if (node && this.annotations.indexOfChild(node) === -1) {
this.annotations.addChild(node); this.annotations.addChild(node);
} }
tool.setEditMode(EditMode.EditNode);
tool.editNode(node); // 关键修复:绘制完成后,确保节点保持可编辑属性
this.selectedShape = node; try {
const props = typeof node.getProperties === 'function' ? node.getProperties() : {};
if (!props.selectable || !props.movable || !props.editable) {
// 如果属性丢失,重新设置
const currentLineStyle = props.linestyle || new LineStyle({
color: this.lineStyle?.color || '#0351ad',
width: this.lineStyle?.width || 2,
pattern: this.getProcessedLinePattern ? this.getProcessedLinePattern(this.lineStyle?.pattern) : []
});
node.setProperties({
...props,
linestyle: currentLineStyle,
selectable: true,
movable: true,
editable: true
});
}
} catch (e) {
console.warn('设置线条节点编辑属性失败:', e);
}
// 修复:绘制完成后,保持编辑模式,让用户可以立即编辑刚绘制的线条
// 用户可以通过点击空白区域或按 ESC 来取消编辑,然后继续绘制新线条
try {
if (this.pencilTool && this.pencilTool.isEnabled() && this.showLineStylePanel) {
// 设置编辑模式,让用户可以选择和编辑刚绘制的线条
this.pencilTool.setEditMode(EditMode.EditNode);
this.pencilTool.editNode(node);
this.selectedShape = node;
}
} catch (e) {
console.warn('设置线条编辑模式失败:', e);
}
this.requestRepaint(); this.requestRepaint();
}); });
} }
...@@ -9455,7 +9460,7 @@ export default { ...@@ -9455,7 +9460,7 @@ export default {
border: none; border: none;
background: linear-gradient(145deg, #f8f9fa 0%, #e9ecef 100%); background: linear-gradient(145deg, #f8f9fa 0%, #e9ecef 100%);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.8); inset 0 1px 0 rgba(255, 255, 255, 0.8);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative; position: relative;
overflow: hidden; overflow: hidden;
...@@ -9476,7 +9481,7 @@ export default { ...@@ -9476,7 +9481,7 @@ export default {
.toolbar-right .el-button:has(.el-icon-edit-outline):hover { .toolbar-right .el-button:has(.el-icon-edit-outline):hover {
background: linear-gradient(145deg, #ff8c00 0%, #ffa500 100%); background: linear-gradient(145deg, #ff8c00 0%, #ffa500 100%);
box-shadow: 0 4px 12px rgba(255, 140, 0, 0.25), box-shadow: 0 4px 12px rgba(255, 140, 0, 0.25),
0 2px 4px rgba(0, 0, 0, 0.1); 0 2px 4px rgba(0, 0, 0, 0.1);
} }
.toolbar-right .el-button:has(.el-icon-edit-outline):hover [class^="el-icon-"] { .toolbar-right .el-button:has(.el-icon-edit-outline):hover [class^="el-icon-"] {
...@@ -9486,7 +9491,7 @@ export default { ...@@ -9486,7 +9491,7 @@ export default {
.toolbar-right .el-button:has(.el-icon-edit-outline).active { .toolbar-right .el-button:has(.el-icon-edit-outline).active {
background: linear-gradient(145deg, #ff8c00 0%, #ffa500 100%); background: linear-gradient(145deg, #ff8c00 0%, #ffa500 100%);
box-shadow: 0 6px 16px rgba(255, 140, 0, 0.3), box-shadow: 0 6px 16px rgba(255, 140, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.2); inset 0 1px 0 rgba(255, 255, 255, 0.2);
} }
.toolbar-right .el-button:has(.el-icon-edit-outline).active [class^="el-icon-"] { .toolbar-right .el-button:has(.el-icon-edit-outline).active [class^="el-icon-"] {
...@@ -9501,7 +9506,7 @@ export default { ...@@ -9501,7 +9506,7 @@ export default {
.toolbar-right .el-button:has(.el-icon-edit):hover { .toolbar-right .el-button:has(.el-icon-edit):hover {
background: linear-gradient(145deg, #28a745 0%, #34ce57 100%); background: linear-gradient(145deg, #28a745 0%, #34ce57 100%);
box-shadow: 0 4px 12px rgba(40, 167, 69, 0.25), box-shadow: 0 4px 12px rgba(40, 167, 69, 0.25),
0 2px 4px rgba(0, 0, 0, 0.1); 0 2px 4px rgba(0, 0, 0, 0.1);
} }
.toolbar-right .el-button:has(.el-icon-edit):hover [class^="el-icon-"] { .toolbar-right .el-button:has(.el-icon-edit):hover [class^="el-icon-"] {
...@@ -9511,7 +9516,7 @@ export default { ...@@ -9511,7 +9516,7 @@ export default {
.toolbar-right .el-button:has(.el-icon-edit).active { .toolbar-right .el-button:has(.el-icon-edit).active {
background: linear-gradient(145deg, #28a745 0%, #34ce57 100%); background: linear-gradient(145deg, #28a745 0%, #34ce57 100%);
box-shadow: 0 6px 16px rgba(40, 167, 69, 0.3), box-shadow: 0 6px 16px rgba(40, 167, 69, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.2); inset 0 1px 0 rgba(255, 255, 255, 0.2);
} }
.toolbar-right .el-button:has(.el-icon-edit).active [class^="el-icon-"] { .toolbar-right .el-button:has(.el-icon-edit).active [class^="el-icon-"] {
...@@ -9530,7 +9535,7 @@ export default { ...@@ -9530,7 +9535,7 @@ export default {
.toolbar-right .el-button:has(.el-icon-s-operation):hover { .toolbar-right .el-button:has(.el-icon-s-operation):hover {
background: linear-gradient(145deg, #9c27b0 0%, #ba68c8 100%); background: linear-gradient(145deg, #9c27b0 0%, #ba68c8 100%);
box-shadow: 0 4px 12px rgba(156, 39, 176, 0.25), box-shadow: 0 4px 12px rgba(156, 39, 176, 0.25),
0 2px 4px rgba(0, 0, 0, 0.1); 0 2px 4px rgba(0, 0, 0, 0.1);
} }
.toolbar-right .el-button:has(.el-icon-s-operation):hover [class^="el-icon-"] { .toolbar-right .el-button:has(.el-icon-s-operation):hover [class^="el-icon-"] {
...@@ -9540,7 +9545,7 @@ export default { ...@@ -9540,7 +9545,7 @@ export default {
.toolbar-right .el-button:has(.el-icon-s-operation).active { .toolbar-right .el-button:has(.el-icon-s-operation).active {
background: linear-gradient(145deg, #9c27b0 0%, #ba68c8 100%); background: linear-gradient(145deg, #9c27b0 0%, #ba68c8 100%);
box-shadow: 0 6px 16px rgba(156, 39, 176, 0.3), box-shadow: 0 6px 16px rgba(156, 39, 176, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.2); inset 0 1px 0 rgba(255, 255, 255, 0.2);
} }
.toolbar-right .el-button:has(.el-icon-s-operation).active [class^="el-icon-"] { .toolbar-right .el-button:has(.el-icon-s-operation).active [class^="el-icon-"] {
...@@ -9563,7 +9568,7 @@ export default { ...@@ -9563,7 +9568,7 @@ export default {
.toolbar-right .el-button:hover { .toolbar-right .el-button:hover {
background: linear-gradient(145deg, #409EFF 0%, #66b1ff 100%); background: linear-gradient(145deg, #409EFF 0%, #66b1ff 100%);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.25), box-shadow: 0 4px 12px rgba(64, 158, 255, 0.25),
0 2px 4px rgba(0, 0, 0, 0.1); 0 2px 4px rgba(0, 0, 0, 0.1);
transform: translateY(-1px); transform: translateY(-1px);
} }
...@@ -9579,7 +9584,7 @@ export default { ...@@ -9579,7 +9584,7 @@ export default {
.toolbar-right .el-button.active { .toolbar-right .el-button.active {
background: linear-gradient(145deg, #409EFF 0%, #66b1ff 100%); background: linear-gradient(145deg, #409EFF 0%, #66b1ff 100%);
box-shadow: 0 6px 16px rgba(64, 158, 255, 0.3), box-shadow: 0 6px 16px rgba(64, 158, 255, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.2); inset 0 1px 0 rgba(255, 255, 255, 0.2);
transform: translateY(-1px); transform: translateY(-1px);
} }
...@@ -9879,27 +9884,27 @@ export default { ...@@ -9879,27 +9884,27 @@ export default {
.CustomGrayScale { .CustomGrayScale {
background: linear-gradient(to right, background: linear-gradient(to right,
rgb(255, 255, 255) 50%, rgb(255, 255, 255) 50%,
/* -4000到181保持白色 */ /* -4000到181保持白色 */
rgb(253, 253, 253) 55%, rgb(253, 253, 253) 55%,
/* 181开始的颜色 */ /* 181开始的颜色 */
rgb(240, 240, 240) 65%, rgb(240, 240, 240) 65%,
/* 到566的颜色 */ /* 到566的颜色 */
rgb(180, 180, 180) 80%, rgb(180, 180, 180) 80%,
/* 566后的过渡色 */ /* 566后的过渡色 */
rgb(6, 6, 6) 100% rgb(6, 6, 6) 100%
/* 最终颜色 */ /* 最终颜色 */
); );
} }
.CustomRedScale { .CustomRedScale {
background: linear-gradient(to right, background: linear-gradient(to right,
#f62814, #f62814,
#dd8b66, #dd8b66,
#d7c692, #d7c692,
#fbfaff, #fbfaff,
#6e6e8a, #6e6e8a,
#323136); #323136);
} }
.BlueWhiteRed { .BlueWhiteRed {
...@@ -9908,25 +9913,25 @@ export default { ...@@ -9908,25 +9913,25 @@ export default {
.Rainbow { .Rainbow {
background: linear-gradient(to right, background: linear-gradient(to right,
#ff0000, #ff0000,
#ffff00, #ffff00,
#00ff00, #00ff00,
#00ffff, #00ffff,
#0000ff, #0000ff,
#ff00ff, #ff00ff,
#ff0000); #ff0000);
} }
.Jet { .Jet {
background: linear-gradient(to right, background: linear-gradient(to right,
#00008f, #00008f,
#0000ff, #0000ff,
#00ffff, #00ffff,
#00ff00, #00ff00,
#ffff00, #ffff00,
#ff7f00, #ff7f00,
#ff0000, #ff0000,
#7f0000); #7f0000);
} }
.Hot { .Hot {
...@@ -10041,7 +10046,7 @@ export default { ...@@ -10041,7 +10046,7 @@ export default {
.scrollbar-track, .scrollbar-track,
.gt-scrollbar-thumb, .gt-scrollbar-thumb,
.gt-scrollbar-track, .gt-scrollbar-track,
/* 仅在地震图可滚动区域内隐藏内部滚动条相关"thumb/track",避免误伤缩略图 */ /* 仅在地震图可滚动区域内隐藏内部滚动条相关"thumb/track",避免误伤缩略图 */
.sync-section [class*="thumb"]:not(.thumb-holder):not(.thumb-title):not(.right-thumb), .sync-section [class*="thumb"]:not(.thumb-holder):not(.thumb-title):not(.right-thumb),
.sync-section [class*="track"] { .sync-section [class*="track"] {
display: none !important; display: none !important;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment