| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055 |
- <!-- 项目输入页面 -->
- <template>
- <div class="project-input-container">
- <!-- 从原代码中移动第一个 tab-pane 的内容 -->
- <div class="project-selector">
- <el-form :inline="true">
- <el-form-item label="当前项目">
- <el-select
- v-model="selectedProjectId"
- disabled
- placeholder="请选择项目"
- style="width: 300px"
- @change="handleProjectConfirm"
- >
- <el-option
- v-for="project in projectOptions"
- :key="project.id"
- :label="project.name"
- :value="project.id"
- />
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button
- v-if="!hasExistingData"
- type="primary"
- @click="handleAddGspr"
- >创建输入项目</el-button
- >
- <el-button v-else type="primary" @click="handleUpdateGspr"
- >更新输入项目</el-button
- >
- <el-button type="danger" @click="handleDeleteItems">删除</el-button>
- </el-form-item>
- </el-form>
- </div>
- <!-- 使用 v-if 控制其余内容的显示 -->
- <template v-if="selectedProjectId">
- <div class="operation-bar">
- <el-form :inline="true" :model="gsprSearchForm" class="search-form">
- <el-form-item label="内容">
- <el-input
- v-model="gsprSearchForm.keyword"
- placeholder="请输入项目名称/技术报告位置"
- style="width: 300px"
- clearable
- />
- </el-form-item>
- <el-form-item label="部门">
- <el-select
- v-model="gsprSearchForm.department"
- placeholder="请选择部门"
- clearable
- >
- <el-option
- v-for="(item, index) in departList"
- :key="index"
- :label="item.label"
- :value="item.label"
- />
- </el-select>
- </el-form-item>
- <!-- 添加时间范围选择器 -->
- <el-form-item label="创建时间">
- <el-date-picker
- v-model="gsprSearchForm.dateRange"
- type="daterange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期"
- value-format="yyyy-MM-dd"
- :picker-options="pickerOptions"
- />
- </el-form-item>
- <!-- 添加内容状态筛选 -->
- <el-form-item label="内容状态">
- <el-select
- v-model="gsprSearchForm.content_empty"
- placeholder="请选择"
- clearable
- >
- <!-- <el-option label="请选择状态" value="null" /> -->
- <el-option label="有内容" :value="false" />
- <el-option label="无内容" :value="true" />
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="handleGsprSearch">搜索</el-button>
- <el-button @click="handleGsprReset">重置</el-button>
- </el-form-item>
- </el-form>
- </div>
- <div>
- <el-button type="primary" @click="handleExport">导出输入项目</el-button>
- <!-- <el-button type="primary" @click="handleDocViewGspr"
- >文档预览</el-button
- > -->
- </div>
- <el-table
- :data="gsprTableData"
- style="width: 100%"
- :default-sort="{ prop: sort_field, order: sort_order }"
- @sort-change="handleSortChange"
- :row-class-name="getRowClassName"
- >
- <el-table-column prop="sequence" label="排序" sortable width="80">
- <template slot-scope="scope">
- <div style="display: flex; align-items: center">
- <span style="margin-right: 10px">{{ scope.row.sequence }}</span>
- <i
- class="el-icon-edit"
- style="cursor: pointer; margin-left: 5px"
- @click="handleEditSequence(scope.row)"
- />
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="name" label="项目名称" sortable>
- <template slot-scope="scope">
- <span v-if="currentModifiedItem === scope.row.id" class="modified-indicator"></span>
- {{ scope.row.name }}
- </template>
- </el-table-column>
- <el-table-column prop="tech_report_location" label="技术报告位置" />
- <el-table-column prop="department" label="部门" sortable />
- <el-table-column prop="content" label="内容" width="450">
- <template slot-scope="scope">
- <div class="content-cell">
- <!-- <el-tooltip
- placement="top-start"
- :disabled="!scope.row.content"
- popper-class="custom-tooltip"
- >
- <template slot="content">
- <div
- class="tooltip-content"
- style="color: #fff !important"
- v-html="scope.row.content"
- ></div>
- </template>
- </el-tooltip> -->
- <span class="content-text">{{
- getPlainText(scope.row.content)
- }}</span>
- <i
- class="el-icon-edit"
- style="cursor: pointer; margin-left: 5px"
- @click="handleEditContent(scope.row)"
- />
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="" label="难度" sortable>
- <template slot-scope="scope">
- <el-rate
- v-model="scope.row.score"
- show-score
- text-color="#ff9900"
- score-template="{value}"
- @change="(value) => rateScope(value, scope.row)"
- />
- </template>
- </el-table-column>
- <el-table-column label="备注" width="200">
- <template slot-scope="scope">
- <div style="display: flex; align-items: center">
- <span style="margin-right: 10px">{{ scope.row.remarks }}</span>
- <i
- class="el-icon-edit"
- style="cursor: pointer; margin-left: 5px"
- @click="handleEditRemark(scope.row)"
- />
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="modified_by" label="创建人" />
- <el-table-column label="操作">
- <template slot-scope="scope">
- <el-button type="primary" @click="handleViewGspr(scope.row)"
- >查看</el-button
- >
- <!-- <el-button type="primary" @click="handleDocViewGspr(scope.row)"
- >文档预览</el-button
- > -->
- <!-- <el-button type="primary" @click="handleEditGspr(scope.row)"
- >编辑</el-button
- >
- <el-button type="danger" @click="handleDeleteGspr(scope.row)"
- >删除</el-button
- > -->
- </template>
- </el-table-column>
- </el-table>
- <div class="pagination-container">
- <el-pagination
- :current-page.sync="currentPage"
- :page-sizes="[30, 50]"
- :page-size="pageSize"
- layout="total, sizes, prev, pager, next, jumper"
- :total="gsprTotal"
- @size-change="handleGsprSizeChange"
- @current-change="handleGsprCurrentChange"
- />
- </div>
- <!-- GSPR表单弹窗 -->
- <el-dialog
- :title="gsprDialogTitle"
- :visible.sync="gsprDialogVisible"
- :close-on-click-modal="false"
- width="700px"
- >
- <el-form
- ref="gsprForm"
- :model="gsprForm"
- :rules="gsprRules"
- label-width="120px"
- >
- <el-form-item label="名称" prop="name">
- <el-input v-model="gsprForm.name" />
- </el-form-item>
- <el-form-item label="技术报告位置" prop="tech_report_location">
- <el-input v-model="gsprForm.tech_report_location" />
- </el-form-item>
- <el-form-item label="部门" prop="department">
- <el-select v-model="gsprForm.department" placeholder="请选择状态">
- <el-option
- v-for="(item, index) in departList"
- :key="index"
- :label="item.label"
- :value="item.label"
- />
- </el-select>
- </el-form-item>
- <!-- <el-form-item label="内容" prop="content">
- <div class="editor-area sticky-editor">
- <ckeditor
- ref="editor"
- v-model="gsprForm.content"
- :config="editorConfig"
- :editorUrl="editorUrl"
- ></ckeditor>
- </div>
- </el-form-item> -->
- </el-form>
- <div slot="footer">
- <el-button @click="gsprDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleGsprSubmit">确定</el-button>
- </div>
- </el-dialog>
- <!-- 添加内容编辑弹窗 -->
- <el-dialog
- title="编辑内容"
- :visible.sync="contentDialogVisible"
- :close-on-click-modal="false"
- width="70%"
- top="10px"
- >
- <!-- <el-form :model="contentForm" ref="contentForm">
- <el-form-item label="内容" prop="content"> -->
- <!-- <el-inputwidth="600px"
- type="textarea"
- v-model="contentForm.content"
- :rows="4"
- placeholder="请输入内容"
- /> -->
- <!-- <ckeditor
- ref="editor"
- v-model="contentForm.content"
- :config="editorConfig"
- :editorUrl="editorUrl"
- ></ckeditor> -->
- <CanvasEditor
- ref="wordEditor"
- :key="keys"
- :edit-mode="Modetype"
- :html-data="htmlData"
- :doc-json="docJson"
- :editor-options="editorOptions"
- @save="save"
- @is-save="getSave"
- />
- <!-- </el-form-item>
- </el-form> -->
- <div slot="footer">
- <el-button @click="contentDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleContentSubmit"
- >确定</el-button
- >
- </div>
- </el-dialog>
- <!-- 查看弹窗 -->
- <el-dialog
- title="查看详情"
- :visible.sync="viewDialogVisible"
- :close-on-click-modal="false"
- width="80%"
- >
- <div class="view-details">
- <el-descriptions :column="2" border>
- <el-descriptions-item label="项目名称">{{
- viewForm.name
- }}</el-descriptions-item>
- <el-descriptions-item label="技术报告位置">{{
- viewForm.tech_report_location
- }}</el-descriptions-item>
- <el-descriptions-item label="部门">{{
- viewForm.department
- }}</el-descriptions-item>
- <el-descriptions-item label="难度">
- <el-rate
- v-model="viewForm.score"
- disabled
- show-score
- text-color="#ff9900"
- score-template="{value}"
- />
- </el-descriptions-item>
- <el-descriptions-item label="备注" :span="2">{{
- viewForm.remarks || "-"
- }}</el-descriptions-item>
- <el-descriptions-item label="内容" :span="2">
- <div class="content-view" v-html="viewForm.content || '-'" />
- </el-descriptions-item>
- </el-descriptions>
- </div>
- </el-dialog>
- <!-- 添加文档预览对话框 -->
- <el-dialog
- title="文档预览"
- :visible.sync="docPreviewDialogVisible"
- :close-on-click-modal="false"
- width="65%"
- class="doc-preview-dialog"
- >
- <div class="doc-preview-header">
- <span>当前文档:</span>
- <el-select
- v-model="docPreviewSel"
- placeholder="请选择文档"
- style="width: 300px"
- @change="handleDocPreviewChange"
- >
- <el-option
- v-for="doc in boundTemplateList"
- :key="doc.id"
- :label="doc.title"
- :value="doc"
- />
- </el-select>
- </div>
- <div v-loading="previewLoading" class="doc-preview-content">
- <div v-if="previewContent" v-html="previewContent" />
- <el-empty v-else description="请选择要预览的文档" />
- </div>
- </el-dialog>
- </template>
- <!-- 未选择项目时显示提示 -->
- <el-empty v-else description="请先选择一个项目" :image-size="200" />
- <!-- 添加删除确认对话框 -->
- <el-dialog
- title="删除确认"
- :visible.sync="deleteDialogVisible"
- width="400px"
- >
- <!-- 添加提示信息区域 -->
- <div class="delete-warning" style="margin-bottom: 20px">
- <i class="el-icon-warning" style="color: #e6a23c; margin-right: 8px" />
- <span
- >此操作将永久删除该项目下的所有输入项目,请输入'DELETE'确认删除</span
- >
- </div>
- <el-form
- ref="deleteForm"
- :model="deleteForm"
- :rules="deleteRules"
- label-width="100px"
- >
- <el-form-item label="确认文本" prop="confirmText">
- <el-input
- v-model="deleteForm.confirmText"
- placeholder="请输入'DELETE'"
- />
- </el-form-item>
- </el-form>
- <span slot="footer" class="dialog-footer">
- <el-button @click="deleteDialogVisible = false">取消</el-button>
- <el-button type="danger" @click="confirmDelete">确认删除</el-button>
- </span>
- </el-dialog>
- <!-- 其余第一个tab的内容... -->
- <!-- 修改序号 -->
- <el-dialog
- v-el-drag-dialog
- :visible.sync="dialogVisible"
- title="修改序号"
- width="30%"
- :close-on-click-modal="false"
- append-to-body
- >
- <el-form :model="sequenceForm" label-width="80px">
- <el-form-item label="序号">
- <el-input-number
- v-model="sequenceForm.sequence"
- :min="0"
- controls-position="right"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="dialogVisible = false">取消</el-button>
- <el-button type="primary" @click="submitSequence">确定</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 添加备注编辑对话框 -->
- <el-dialog
- title="编辑备注"
- :visible.sync="remarkDialogVisible"
- :close-on-click-modal="false"
- width="500px"
- >
- <el-form :model="remarkForm">
- <el-form-item label="备注">
- <el-input
- v-model="remarkForm.remarks"
- type="textarea"
- :rows="4"
- placeholder="请输入备注"
- />
- </el-form-item>
- </el-form>
- <div slot="footer">
- <el-button @click="remarkDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleRemarkSubmit">确定</el-button>
- </div>
- </el-dialog>
- </div>
- </template>
- <script>
- import {
- projectListFromTemplate,
- projectAdd,
- departments,
- projectUpdate,
- projectDelete,
- create_items,
- delete_items,
- update_items,
- rateScope,
- sequence,
- projectList,
- } from "@/api/knowledge";
- import {
- searchProject,
- getProjectInfo,
- searchTemplateList,
- } from "@/api/project";
- import axios from "axios";
- import elDragDialog from "@/directive/el-drag-dialog";
- import CanvasEditor from "@/components/CanvasEditor/index"; // 确保路径正确
- export default {
- name: "ProjectInput",
- components: {
- CanvasEditor, // 使用驼峰式注册
- // 或者使用短横线式注册
- // 'canvas-editor': CanvasEditor,
- },
- directives: { elDragDialog },
- data() {
- // 自定义验证规则
- const validateDeleteConfirm = (rule, value, callback) => {
- if (value !== "DELETE") {
- callback(new Error("请输入正确的确认文本"));
- } else {
- callback();
- }
- };
- return {
- pickerOptions: {
- shortcuts: [
- {
- text: "最近一周",
- onClick(picker) {
- const end = new Date();
- const start = new Date();
- start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
- picker.$emit("pick", [start, end]);
- },
- },
- {
- text: "最近一个月",
- onClick(picker) {
- const end = new Date();
- const start = new Date();
- start.setMonth(start.getMonth() - 1);
- picker.$emit("pick", [start, end]);
- },
- },
- ],
- },
- selectedProjectId: null,
- projectOptions: [],
- gsprTableData: [],
- gsprSearchForm: {
- keyword: "",
- department: "",
- content_empty: null,
- },
- gsprTotal: 0,
- departList: [],
- sort_field: "sequence",
- sort_order: "asc",
- hasExistingData: false,
- gsprDialogVisible: false,
- gsprDialogTitle: "",
- gsprForm: {
- id: "",
- name: "",
- tech_report_locations: "",
- department: "",
- content: "",
- remarks: "",
- sequence: 0,
- },
- gsprRules: {
- name: [{ required: true, message: "请输入名称", trigger: "blur" }],
- tech_report_location: [
- { required: true, message: "请输入技术报告位置", trigger: "blur" },
- ],
- department: [
- { required: true, message: "请输入部门", trigger: "blur" },
- ],
- status: [{ required: true, message: "请选择状态", trigger: "change" }],
- },
- editorOptions: {},
- contentDialogVisible: false,
- contentForm: {
- id: "",
- content: "",
- name: "",
- department: "",
- },
- /* 删除逻辑 */
- deleteDialogVisible: false,
- deleteForm: {
- confirmText: "",
- },
- deleteRules: {
- confirmText: [
- { required: true, message: "请输入确认文本", trigger: "blur" },
- { validator: validateDeleteConfirm, trigger: "blur" },
- ],
- },
- editorUrl: "/ckeditor/ckeditor.js",
- editorConfig: {
- language: "zh-cn",
- height: "300px",
- },
- dialogVisible: false,
- sequenceForm: {
- sequence: 1,
- id: null,
- },
- remarkDialogVisible: false,
- remarkForm: {
- id: "",
- remarks: "",
- },
- /* 已绑定项目 */
- documentIds: [],
- templateLocations: [], // 存储传递过来的位置信息
- matchedContents: {}, // 存储技术报告位置匹配的内容
- Modetype: "edit",
- htmlData: "", // 编辑器html数据
- docJson: null, // 编辑器getValue数据
- keys: new Date().getTime(), // 给编辑器赋值不刷新的时候,改变key
- /* 查看 */
- viewDialogVisible: false,
- viewForm: {
- name: "",
- tech_report_location: "",
- department: "",
- score: 0,
- remarks: "",
- content: "",
- },
- /* 文档预览 */
- docPreviewDialogVisible: false,
- previewData: {
- name: "",
- tech_report_location: "",
- interpretation: "",
- content: "",
- },
- /* 文档列表 */
- boundTemplateList: [],
- docPreviewSel: "",
- previewLoading: false,
- previewContent: "",
- currentPage: 1,
- pageSize: 30,
- // 修改状态跟踪
- currentModifiedItem: null, // 当前高亮的项ID(只保持一个)
- originalData: new Map(), // 存储原始数据用于对比
- clearTimer: null, // 自动清理定时器
- };
- },
- mounted() {
- this.fetchProjects().then(() => {
- // Get project ID from URL query parameter
- const urlProjectId = this.$route.query.id;
- if (urlProjectId) {
- this.selectedProjectId = parseInt(urlProjectId);
- this.handleProjectConfirm();
- }
- });
- // 获取并处理传递过来的模板位置数据
- /* const projectDataStr = this.$route.query.projectData;
- if (projectDataStr) {
- try {
- const projectData = JSON.parse(decodeURIComponent(projectDataStr));
- this.templateLocations = projectData.locations;
- // 创建位置到内容的映射
- this.matchedContents = this.templateLocations.reduce((acc, loc) => {
- if (loc.type === "template_location") {
- // 移除位置标记中的括号以便匹配
- const cleanLocation = loc.tech_report_location;
- acc[cleanLocation] = loc;
- }
- return acc;
- }, {});
- } catch (error) {
- console.error("解析项目数据失败:", error);
- }
- } */
- this.init();
- },
- methods: {
- /* 当前文档选择 */
- // 处理文档选择变化
- async handleDocPreviewChange(docId) {
- if (!docId) {
- this.previewContent = "";
- return;
- }
- this.previewLoading = true;
- try {
- const docData = JSON.parse(docId.data);
- // 假设这是获取文档预览内容的API
- this.previewContent = docData[0].content;
- } catch (error) {
- this.$message.error(
- "获取文档预览失败:" + (error.message || "未知错误")
- );
- this.previewContent = "";
- } finally {
- this.previewLoading = false;
- }
- },
- /* 处理文字 */
- getPlainText(html) {
- if (!html) return "";
- // 创建临时 DOM 元素来解析 HTML
- const temp = document.createElement("div");
- temp.innerHTML = html;
- // 获取纯文本内容
- const plainText = temp.textContent || temp.innerText;
- // 截断文本
- return plainText.length > 50
- ? plainText.substring(0, 50) + "..."
- : plainText;
- },
- // 添加 CanvasEditor 相关的方法
- save(data) {
- // 处理保存逻辑
- console.log("保存数据:", data);
- // 这里可以添加具体的保存实现
- },
- getSave(isSaved) {
- // 处理保存状态
- console.log("是否已保存:", isSaved);
- // 这里可以添加保存状态的处理逻辑
- },
- // 修改 handleEditContent 方法,添加编辑器数据的处理
- handleEditContent(row) {
- const selectedProject = this.projectOptions.find(
- (p) => p.id === this.selectedProjectId
- );
- // 保存原始数据用于对比
- this.saveOriginalData(row);
- // 处理 content 内容,将文本格式转换为 HTML 并清理空样式
- const processedContent = this.cleanEmptyStyles(
- this.convertTextToHtml(row.content)
- );
- this.contentForm = {
- sequence: row.sequence,
- project: selectedProject.id,
- project_name: selectedProject.name,
- id: row.id,
- tech_report_location: row.tech_report_location,
- content: row.content, // processedContent,
- name: row.name,
- department: row.department,
- };
- if (row.font_style) {
- this.editorOptions = row.font_style;
- } else {
- this.editorOptions = {
- font: "Arial",
- size: 14,
- bold: false,
- align: "left",
- };
- }
- // 设置编辑器数据
- this.htmlData = processedContent;
- this.Modetype = "edit";
- this.keys = new Date().getTime(); // 强制刷新编辑器
- this.contentDialogVisible = true;
- },
- // 处理内容中的a标签,移除a标签但保持表头样式
- processContentForSave(htmlContent) {
- try {
- if (!htmlContent) return htmlContent;
-
- console.log('开始处理内容,移除a标签但保持表头样式:', htmlContent);
-
- // 创建临时DOM元素来解析HTML
- const tempDiv = document.createElement('div');
- tempDiv.innerHTML = htmlContent;
-
- // 处理所有表格
- const tables = tempDiv.querySelectorAll('table');
- tables.forEach(table => {
- // 处理表头行
- const headerRows = table.querySelectorAll('thead tr, tr:first-child');
- headerRows.forEach(headerRow => {
- // 确保表头有背景色
- if (!headerRow.style.backgroundColor) {
- headerRow.style.backgroundColor = '#f5f5f5';
- }
-
- // 获取表头单元格
- const headerCells = headerRow.querySelectorAll('th, td');
-
- // 找到第一个没有a标签的表头单元格作为样式参考
- let referenceCell = null;
- let referenceStyle = null;
-
- for (let cell of headerCells) {
- if (!cell.querySelector('a')) {
- referenceCell = cell;
- referenceStyle = window.getComputedStyle(cell);
- break;
- }
- }
-
- // 如果没找到参考单元格,使用默认样式
- if (!referenceStyle) {
- referenceStyle = {
- getPropertyValue: (prop) => {
- const defaults = {
- 'font-weight': 'bold',
- 'font-size': '14px',
- 'font-family': 'Arial, sans-serif',
- 'color': '#000000',
- 'background-color': '#f5f5f5',
- 'text-align': 'left'
- };
- return defaults[prop] || '';
- }
- };
- }
-
- console.log('参考样式:', {
- fontWeight: referenceStyle.getPropertyValue('font-weight'),
- fontSize: referenceStyle.getPropertyValue('font-size'),
- fontFamily: referenceStyle.getPropertyValue('font-family'),
- color: referenceStyle.getPropertyValue('color'),
- backgroundColor: referenceStyle.getPropertyValue('background-color')
- });
-
- // 处理所有表头单元格
- headerCells.forEach(cell => {
- // 确保表头单元格有背景色
- if (!cell.style.backgroundColor) {
- cell.style.backgroundColor = '#f5f5f5';
- }
-
- // 处理单元格内的超链接
- const cellLinks = cell.querySelectorAll('a');
- cellLinks.forEach(cellLink => {
- const span = document.createElement('span');
- span.textContent = cellLink.textContent;
-
- // 直接应用参考样式
- span.style.fontWeight = referenceStyle.getPropertyValue('font-weight') || 'bold';
- span.style.fontSize = referenceStyle.getPropertyValue('font-size') || '14px';
- span.style.fontFamily = referenceStyle.getPropertyValue('font-family') || 'Arial, sans-serif';
- span.style.color = referenceStyle.getPropertyValue('color') || '#000000';
- span.style.textAlign = referenceStyle.getPropertyValue('text-align') || 'left';
-
- // 移除链接样式
- span.style.textDecoration = 'none';
- span.style.cursor = 'default';
-
- console.log('替换a标签,应用样式:', {
- fontWeight: span.style.fontWeight,
- fontSize: span.style.fontSize,
- fontFamily: span.style.fontFamily,
- color: span.style.color,
- backgroundColor: span.style.backgroundColor
- });
-
- cellLink.parentNode.replaceChild(span, cellLink);
- });
- });
- });
-
- // 处理表格单元格中的超链接(非表头)
- const cellLinks = table.querySelectorAll('td a, tbody a');
- cellLinks.forEach(cellLink => {
- const span = document.createElement('span');
- span.textContent = cellLink.textContent;
-
- // 保持单元格的默认样式
- span.style.color = 'inherit';
- span.style.textDecoration = 'none';
- span.style.cursor = 'default';
-
- cellLink.parentNode.replaceChild(span, cellLink);
- });
- });
-
- // 处理表格外的超链接
- const links = tempDiv.querySelectorAll('a:not(table a)');
- links.forEach(link => {
- const span = document.createElement('span');
- span.textContent = link.textContent;
-
- // 获取父元素的样式
- const parentElement = link.parentElement;
- if (parentElement) {
- const parentStyles = window.getComputedStyle(parentElement);
-
- // 复制重要的样式属性
- const stylesToCopy = [
- 'background-color', 'background', 'font-family', 'font-size',
- 'font-weight', 'font-style', 'text-align', 'padding', 'margin',
- 'border', 'border-color', 'border-width', 'border-style',
- 'color', 'text-decoration'
- ];
-
- stylesToCopy.forEach(styleProp => {
- const value = parentStyles.getPropertyValue(styleProp);
- if (value && value !== 'initial' && value !== 'inherit' && value !== 'transparent') {
- span.style.setProperty(styleProp, value);
- }
- });
- }
-
- // 设置文本样式为普通文本,移除链接样式
- span.style.textDecoration = 'none';
- span.style.cursor = 'default';
-
- // 替换超链接元素
- link.parentNode.replaceChild(span, link);
- });
-
- const result = tempDiv.innerHTML;
- console.log('处理完成后的HTML:', result);
- return result;
- } catch (error) {
- console.error('处理内容时出错:', error);
- return htmlContent; // 如果处理失败,返回原始内容
- }
- },
- // 修改 handleContentSubmit 方法
- async handleContentSubmit() {
- try {
- // 获取编辑器的最新内容
- const editorContent = this.$refs.wordEditor.editorRef.command.getHTML();
-
- // 获取当前的字体样式配置
- const fontStyle = this.editorOptions || {
- font: 'Arial',
- size: 14,
- align: 'left'
- };
- // 创建样式字符串
- const styleString = `font-family: ${fontStyle.font}; font-size: ${fontStyle.size}px; text-align: ${fontStyle.align};}`;
-
- // 处理表格内容
- const processedContent = editorContent.main.replace(
- /<table[^>]*>([\s\S]*?)<\/table>/g,
- (tableMatch) => {
- // 首先处理表格级别的空白和换行
- const cleanTableMatch = tableMatch
- .replace(/>\s+</g, '><') // 移除标签之间的空白
- .replace(/\n/g, '') // 移除所有换行符
- .replace(/<div style="text-align: justify;">([\s\S]*?)<\/div>/g, '$1') // 移除对齐div标签
- .replace(/<a[^>]*>([\s\S]*?)<\/a>/g, (match, content) => {
- // 使用配置的字体样式替换超链接文本
- return `<span style="${styleString}">${content}</span>`;
- });
- return cleanTableMatch.replace(
- /(<td[^>]*>)([\s\S]*?)(<\/td>)/g,
- (tdMatch, tdOpen, content, tdClose) => {
- // 处理单元格内容
- const cleanContent = content
- .replace(/<div style="text-align: justify;">([\s\S]*?)<\/div>/g, '$1') // 移除对齐div标签
- .replace(/<a[^>]*>([\s\S]*?)<\/a>/g, (match, linkContent) => {
- // 使用配置的字体样式替换超链接文本
- return `<span style="${styleString}">${linkContent}</span>`;
- })
- .replace(/\n/g, '') // 移除所有换行
- .replace(/\s+/g, ' ') // 多个空格变成一个
- .replace(/^\s+|\s+$/g, '') // 移除首尾空格
- .replace(/—\s+/g, '— ') // 处理破折号后的空格
- .replace(/\s+—/g, ' —') // 处理破折号前的空格
- .replace(/\s*([,.])\s*/g, '$1 '); // 处理标点符号周围的空格
- return tdOpen + cleanContent + tdClose;
- }
- );
- }
- );
- // 更新 contentForm
- this.contentForm.content = this.processContentForSave(editorContent.main);
- const submitData = {
- ...this.contentForm,
- id: this.contentForm.id,
- content: this.contentForm.content,
- font_style: this.editorOptions // 保存字体样式配置
- };
-
- await projectUpdate(submitData);
- this.$message.success("内容更新成功");
- this.contentDialogVisible = false;
-
- // 检查数据是否有变化,如果有则标记为已修改
- const originalData = this.originalData.get(this.contentForm.id);
- if (this.checkDataChanged(submitData, originalData)) {
- this.markAsModified(this.contentForm.id);
- }
-
- this.fetchGsprData();
- } catch (error) {
- this.$message.error("内容更新失败");
- }
- },
- // 修改现有的 handleEditRemark 方法
- handleEditRemark(row) {
- // 保存原始数据用于对比
- this.saveOriginalData(row);
-
- // 只复制需要的字段
- this.remarkForm = {
- id: row.id,
- remarks: row.remarks || "",
- sequence: row.sequence,
- project: row.project,
- content: row.content,
- name:row.name,
- department:row.department
- };
- this.remarkDialogVisible = true;
- },
- // 修改备注提交方法
- async handleRemarkSubmit() {
- try {
- console.log(this.remarkForm);
- // 只更新必要的字段
- const updateData = {
- ...this.remarkForm,
- remarks: this.remarkForm.remarks,
- id: this.remarkForm.id,
- };
- await projectUpdate(updateData);
- this.$message.success("备注更新成功");
- this.remarkDialogVisible = false;
-
- // 检查数据是否有变化,如果有则标记为已修改
- const originalData = this.originalData.get(this.remarkForm.id);
- if (this.checkDataChanged(updateData, originalData)) {
- this.markAsModified(this.remarkForm.id);
- }
-
- this.fetchGsprData();
- } catch (error) {
- this.$message.error("备注更新失败");
- }
- },
- /* 修改评分 */
- async rateScope(value, row) {
- try {
- // 保存原始数据用于对比
- if (!this.originalData.has(row.id)) {
- this.saveOriginalData(row);
- }
-
- const response = await rateScope({
- id: row.id,
- score: value,
- });
- if (response.status === 200) {
- this.$message.success("评分更新成功");
- // 更新本地数据
- const index = this.gsprTableData.findIndex(
- (item) => item.id === row.id
- );
- if (index !== -1) {
- this.gsprTableData[index].score = value;
- }
-
- // 检查数据是否有变化,如果有则标记为已修改
- const originalData = this.originalData.get(row.id);
- if (originalData && originalData.score !== value) {
- this.markAsModified(row.id);
- }
- } else {
- throw new Error(response.message || "评分更新失败");
- }
- } catch (error) {
- this.$message.error(error.message || "评分更新失败");
- // 恢复原始评分
- row.score = row.score || 0;
- }
- },
- /* 修改序号 */
- handleEditSequence(row) {
- // 保存原始数据用于对比
- this.saveOriginalData(row);
-
- this.sequenceForm.sequence = row.sequence;
- this.sequenceForm.id = row.id;
- this.dialogVisible = true;
- },
- // 提交序号修改
- async submitSequence() {
- try {
- await sequence({
- id: this.sequenceForm.id,
- new_sequence: this.sequenceForm.sequence,
- });
- this.$message.success("修改序号成功");
- this.dialogVisible = false;
-
- // 检查数据是否有变化,如果有则标记为已修改
- const originalData = this.originalData.get(this.sequenceForm.id);
- if (originalData && originalData.sequence !== this.sequenceForm.sequence) {
- this.markAsModified(this.sequenceForm.id);
- }
-
- // 重新加载数据
- this.fetchGsprData();
- } catch (error) {
- this.$message.error("修改序号失败:" + error.message);
- }
- },
- /* 项目导出 */
- async handleExport() {
- const loading = this.$loading({
- lock: true,
- text: "导出中...",
- });
- try {
- const response = await axios({
- url: `${process.env.VUE_APP_BASE_API}/project-input/export`,
- method: "post",
- data: { project_id: this.selectedProjectId },
- headers: {
- "Content-Type": "application/json",
- Authorization: "Bearer " + localStorage.getItem("token"), // 根据实际token获取方式调整
- },
- });
- // 检查响应数据
- if (
- !response.data ||
- !response.data.data ||
- !response.data.data.data.file_content
- ) {
- throw new Error("导出失败:返回数据格式错误");
- }
- // 获取文件内容和文件名
- const { file_content, file_name } = response.data.data.data;
- // 将base64转换为blob
- const byteCharacters = atob(file_content);
- const byteNumbers = new Array(byteCharacters.length);
- for (let i = 0; i < byteCharacters.length; i++) {
- byteNumbers[i] = byteCharacters.charCodeAt(i);
- }
- const byteArray = new Uint8Array(byteNumbers);
- const blob = new Blob([byteArray], {
- type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
- });
- // 下载文件
- const link = document.createElement("a");
- link.href = window.URL.createObjectURL(blob);
- link.download = file_name;
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- window.URL.revokeObjectURL(link.href);
- this.$message.success(response.data.message || "导出成功");
- } catch (error) {
- console.error("导出错误:", error);
- let errorMessage = "导出失败,请稍后重试";
- if (error.response) {
- switch (error.response.status) {
- case 401:
- errorMessage = "导出失败:未授权,请重新登录";
- break;
- case 403:
- errorMessage = "导出失败:没有导出权限";
- break;
- case 404:
- errorMessage = "导出失败:导出接口不存在";
- break;
- case 500:
- errorMessage = "导出失败:服务器内部错误";
- break;
- default:
- errorMessage = `导出失败:${error.response.status}`;
- }
- }
- this.$message.error(errorMessage);
- } finally {
- loading.close();
- }
- },
- /* 删除 */
- async handleDeleteGspr(row) {
- try {
- await this.$confirm("确认删除该记录?", "提示", {
- type: "warning",
- });
- // 替换为实际的删除API调用
- await projectDelete({ id: row.id });
- this.$message.success("删除成功");
- this.fetchGsprData();
- } catch (error) {
- if (error !== "cancel") {
- this.$message.error("删除失败");
- }
- }
- },
- /* 文档预览 */
- async handleDocViewGspr(row) {
- try {
- const loading = this.$loading({
- lock: true,
- text: "加载文档预览...",
- spinner: "el-icon-loading",
- background: "rgba(0, 0, 0, 0.7)",
- });
- searchTemplateList({ ids: this.documentIds.join(",") || 0 }).then(
- (res) => {
- if (res.status !== 200) return;
- this.boundTemplateList = res.data;
- loading.close();
- this.docPreviewDialogVisible = true;
- }
- );
- } catch (error) {
- loading.close();
- this.$message.error(
- "获取文档预览失败:" + (error.message || "未知错误")
- );
- }
- },
- /* 查看 */
- handleViewGspr(row) {
- this.viewForm = {
- name: row.name,
- tech_report_location: row.tech_report_location,
- department: row.department,
- score: row.score || 0,
- remarks: row.remarks,
- content: row.content,
- };
- this.viewDialogVisible = true;
- },
- /* 编辑 */
- handleEditGspr(row) {
- this.gsprDialogTitle = "编辑文献项目";
- this.gsprForm = { ...row };
- this.gsprDialogVisible = true;
- },
- // 修改回显方法,解析样式
- convertTextToHtml(text) {
- if (!text) return "";
- let html = text;
- // 处理表格标记(包含边框样式)
- html = html.replace(
- /\[表格\|style=(.*?)\|class=(.*?)\]([\s\S]*?)(?=\[|$)/g,
- (match, tableStyle, tableClass, tableContent) => {
- const rows = tableContent.trim().split("\n");
- let tableHtml = `<table style="${tableStyle}" class="${tableClass}">`;
- rows.forEach((row) => {
- tableHtml += "<tr>";
- const cells = row.split("|").map((cell) => {
- const cellMatch = cell.match(
- /(.*?)(?:\[style=(.*?)\|class=(.*?)\])?$/
- );
- if (cellMatch) {
- const [_, content, style = "", className = ""] = cellMatch;
- return `<td style="${style}" class="${className}">${content.trim()}</td>`;
- }
- return `<td>${cell.trim()}</td>`;
- });
- tableHtml += cells.join("") + "</tr>";
- });
- tableHtml += "</table>";
- return tableHtml;
- }
- );
- // 2. 处理图片标记(保持不变)
- html = html.replace(
- /\[图片:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
- (match, src, style, className) => {
- return `<img src="${src}" alt="图片" style="${style}" class="${className}">`;
- }
- );
- // 3. 处理文本样式(保持不变)
- html = html.replace(
- /\[文本:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
- (match, content, style, className) => {
- return `<span style="${style}" class="${className}">${content}</span>`;
- }
- );
- // 4. 处理换行
- html = html.replace(/\n/g, "<br>");
- return html;
- },
- // 添加辅助方法:清理空样式属性
- cleanEmptyStyles(html) {
- return html
- .replace(/style=""/g, "")
- .replace(/class=""/g, "")
- .replace(/\s+>/g, ">");
- },
- /* */
- /* async handleContentSubmit() {
- try {
- // 创建一个函数来处理富文本内容,保留样式
- const processContent = (htmlContent) => {
- const tempDiv = document.createElement("div");
- tempDiv.innerHTML = htmlContent;
- // 处理图片:保留样式属性
- const images = tempDiv.getElementsByTagName("img");
- Array.from(images).forEach((img) => {
- const style = img.getAttribute("style") || "";
- const className = img.getAttribute("class") || "";
- const imgPlaceholder = `[图片:${img.src}|style=${style}|class=${className}]`;
- img.replaceWith(document.createTextNode(imgPlaceholder));
- });
- // 处理表格:保留完整的样式属性,包括边框
- const tables = tempDiv.getElementsByTagName("table");
- Array.from(tables).forEach((table) => {
- const tableStyle = table.getAttribute("style") || "";
- const tableClass = table.getAttribute("class") || "";
- const tableBorder = table.getAttribute("border") || "";
- const rows = Array.from(table.rows).map((row) => {
- return Array.from(row.cells)
- .map((cell) => {
- const cellStyle = cell.getAttribute("style") || "";
- const cellClass = cell.getAttribute("class") || "";
- const cellBorder = cell.getAttribute("border") || "";
- const content = cell.textContent.trim();
- // 合并所有样式属性
- const cellStyles = [
- cellStyle,
- cellBorder ? `border: ${cellBorder}px solid #000;` : "",
- ]
- .filter(Boolean)
- .join(" ");
- return `${content}${
- cellStyles || cellClass
- ? `[style=${cellStyles}|class=${cellClass}]`
- : ""
- }`;
- })
- .join("|");
- });
- // 合并表格的样式属性
- const tableStyles = [
- tableStyle,
- tableBorder ? `border: ${tableBorder}px solid #000;` : "",
- ]
- .filter(Boolean)
- .join(" ");
- const tablePlaceholder = `[表格|style=${tableStyles}|class=${tableClass}]\n${rows.join(
- "\n"
- )}`;
- table.replaceWith(document.createTextNode(tablePlaceholder));
- });
- // 处理文本样式
- const styledSpans = tempDiv.getElementsByTagName("span");
- Array.from(styledSpans).forEach((span) => {
- const style = span.getAttribute("style") || "";
- const className = span.getAttribute("class") || "";
- if (style || className) {
- const content = span.textContent;
- span.replaceWith(
- document.createTextNode(
- `[文本:${content}|style=${style}|class=${className}]`
- )
- );
- }
- });
- return tempDiv.textContent || tempDiv.innerText;
- };
- const submitData = {
- ...this.contentForm,
- content: processContent(this.contentForm.content),
- };
- await projectUpdate(submitData);
- this.$message.success("内容更新成功");
- this.contentDialogVisible = false;
- this.fetchGsprData();
- } catch (error) {
- this.$message.error("内容更新失败");
- }
- }, */
- // 同样修改handleGsprSubmit方法
- async handleGsprSubmit() {
- try {
- await this.$refs.gsprForm.validate();
- // 处理富文本内容
- const processContent = (htmlContent) => {
- const tempDiv = document.createElement("div");
- tempDiv.innerHTML = htmlContent;
- // 处理图片
- const images = tempDiv.getElementsByTagName("img");
- Array.from(images).forEach((img) => {
- const imgPlaceholder = `[图片:${img.src}]`;
- img.replaceWith(document.createTextNode(imgPlaceholder));
- });
- // 处理表格
- const tables = tempDiv.getElementsByTagName("table");
- Array.from(tables).forEach((table) => {
- const rows = Array.from(table.rows).map((row) =>
- Array.from(row.cells)
- .map((cell) => cell.textContent.trim())
- .join(" | ")
- );
- const tablePlaceholder = "[表格]\n" + rows.join("\n");
- table.replaceWith(document.createTextNode(tablePlaceholder));
- });
- return tempDiv.textContent || tempDiv.innerText;
- };
- // 创建新对象避免修改原始数据
- const submitData = {
- ...this.gsprForm,
- content: processContent(this.gsprForm.content),
- };
- if (this.gsprForm.id) {
- await projectUpdate(submitData);
- } else {
- await projectAdd(submitData);
- }
- this.$message.success(this.gsprForm.id ? "更新成功" : "创建成功");
- this.gsprDialogVisible = false;
- this.fetchGsprData();
- } catch (error) {
- if (error !== false) {
- this.$message.error("保存失败");
- }
- }
- },
- /* */
- // async handleGsprSubmit() {
- // try {
- // await this.$refs.gsprForm.validate()
- // if (this.gsprForm.id) {
- // await projectUpdate(this.gsprForm)
- // } else {
- // await projectAdd(this.gsprForm)
- // }
- // this.$message.success(this.gsprForm.id ? '更新成功' : '创建成功')
- // this.gsprDialogVisible = false
- // this.fetchGsprData()
- // } catch (error) {
- // if (error !== false) {
- // this.$message.error('保存失败')
- // }
- // }
- // },
- /* 初始化项目列表 */
- async fetchProjects() {
- try {
- const response = await searchProject({ page: 1, pageSize: 999 });
- if (response.status === 200) {
- this.projectOptions = response.data.dataList;
- // 如果有当前项目ID但没有选中项目,自动选中
- // 如果有当前项目ID但没有选中项目,自动选中
- if (!this.selectedProjectId && this.$route.query.id) {
- this.selectedProjectId = parseInt(this.$route.query.id);
- this.handleProjectConfirm();
- }
- }
- } catch (error) {
- this.$message.error("获取项目列表失败");
- }
- },
- async handleProjectConfirm() {
- if (!this.selectedProjectId) {
- this.$message.warning("请先选择一个项目");
- return;
- }
- const selectedProject = this.projectOptions.find(
- (p) => p.id === this.selectedProjectId
- );
- if (!selectedProject) {
- this.$message.error("未找到选中的项目");
- return;
- }
- this.$store.dispatch("project/setCurrentProject", {
- id: selectedProject.id,
- name: selectedProject.name,
- });
- localStorage.setItem(
- "currentProject",
- JSON.stringify({
- id: selectedProject.id,
- name: selectedProject.name,
- })
- );
- // 恢复该项目上次的分页设置
- try {
- const savedPage = Number(
- localStorage.getItem(`gspr_page_${this.selectedProjectId}`)
- );
- const savedPageSize = Number(
- localStorage.getItem(`gspr_page_size_${this.selectedProjectId}`)
- );
- if (!Number.isNaN(savedPage) && savedPage > 0) {
- this.currentPage = savedPage;
- }
- if (!Number.isNaN(savedPageSize) && savedPageSize > 0) {
- this.pageSize = savedPageSize;
- }
- } catch (e) {
- // 忽略本地存储异常
- }
- this.gsprSearchForm.project_id = this.selectedProjectId;
- this.fetchGsprData();
- },
- // 创建项目
- async handleAddGspr() {
- if (!this.selectedProjectId) {
- this.$message.warning("请先选择一个项目");
- return;
- }
- const loading = this.$loading({
- lock: true,
- text: "正在创建项目...",
- spinner: "el-icon-loading",
- background: "rgba(0, 0, 0, 0.7)",
- });
- try {
- const res = await projectListFromTemplate(
- /* create_items */ {
- project_id: this.selectedProjectId,
- }
- );
- if (res.status === 200) {
- this.$message.success("输入项目创建成功");
- await this.fetchGsprData();
- }
- } catch (error) {
- this.$message.error("创建失败: " + (error.message || "未知错误"));
- } finally {
- loading.close();
- }
- },
- // 更新项目
- async handleUpdateGspr() {
- if (!this.selectedProjectId) {
- this.$message.warning("请先选择一个项目");
- return;
- }
- const selectedProject = this.projectOptions.find(
- (p) => p.id === this.selectedProjectId
- );
- try {
- await this.$confirm(
- "此操作将更新当前项目的所有输入项目,是否继续?",
- "提示",
- {
- confirmButtonText: "确定",
- cancelButtonText: "取消",
- type: "warning",
- }
- );
- const loading = this.$loading({
- lock: true,
- text: "正在更新项目...",
- spinner: "el-icon-loading",
- background: "rgba(0, 0, 0, 0.7)",
- });
- try {
- const res = await projectListFromTemplate(
- /* update_items */ {
- // 假设有一个update_items的API
- project_id: selectedProject.id,
- /* project_name: selectedProject.name */
- }
- );
- console.log(res);
- if (res.status !== 200) {
- throw new Error("更新失败");
- }
- this.$message.success(`输入项目更新成功`);
- await this.fetchGsprData();
- } finally {
- loading.close();
- }
- } catch (error) {
- if (error !== "cancel") {
- this.$message.error("更新失败: " + (error.message || "未知错误"));
- }
- }
- },
- // 删除项目
- async handleDeleteItems() {
- if (!this.selectedProjectId) {
- this.$message.warning("请先选择一个项目");
- return;
- }
- const selectedProject = this.projectOptions.find(
- (p) => p.id === this.selectedProjectId
- );
- this.$confirm(
- `您确定要删除项目 "${selectedProject.name}" 的所有输入项目吗?此操作不可恢复。`,
- "警告",
- {
- confirmButtonText: "继续删除",
- cancelButtonText: "取消",
- type: "warning",
- }
- )
- .then(() => {
- this.deleteForm.confirmText = "";
- this.deleteDialogVisible = true;
- })
- .catch(() => {
- this.$message({
- type: "info",
- message: "已取消删除",
- });
- });
- },
- // 确认删除
- confirmDelete() {
- this.$refs.deleteForm.validate(async (valid) => {
- if (!valid) {
- return false;
- }
- try {
- const loading = this.$loading({
- lock: true,
- text: "正在删除...",
- spinner: "el-icon-loading",
- background: "rgba(0, 0, 0, 0.7)",
- });
- const response = await delete_items({
- project_id: this.selectedProjectId,
- });
- loading.close();
- if (response.status === 200) {
- this.$message.success("删除成功");
- this.deleteDialogVisible = false;
- // 重新加载数据
- this.fetchGsprData();
- } else {
- this.$message.error(response.message || "删除失败");
- }
- } catch (error) {
- this.$message.error("删除失败:" + (error.message || "未知错误"));
- }
- });
- },
- // 修改获取数据的方法
- async fetchGsprData() {
- this.gsprTableData = [];
- if (!this.selectedProjectId) {
- this.$message.warning("请先选择项目");
- return;
- }
- const loading = this.$loading({
- lock: true,
- text: "加载中...",
- spinner: "el-icon-loading",
- background: "rgba(0, 0, 0, 0.7)",
- });
- try {
- const params = {
- project_id: this.selectedProjectId,
- page: this.currentPage,
- page_size: this.pageSize,
- sort_field: this.sort_field,
- sort_order: this.sort_order,
- ...this.gsprPageForm,
- ...this.gsprSearchForm,
- start_date: this.gsprSearchForm.dateRange?.[0] || null,
- end_date: this.gsprSearchForm.dateRange?.[1] || null,
- };
- const response = await projectList(params);
- if (!response || !response.data) {
- throw new Error("API响应数据格式错误");
- }
- response.data.list.map((el) => {
- if (el.score == null) {
- el.score = 0;
- }
- });
- const responseData = response.data;
- // 过滤掉 tech_report_locations 为空的数据
- /* const filteredItems = responseData.items
- .filter(item => item.tech_report_locations)
- .map(item => ({
- id: item.id,
- location: item.location || '',
- tech_report_location: item.tech_report_locations || '',
- name: item.name || '',
- department: item.department || '',
- sequence: item.sequence || 0,
- remarks: item.remarks || '',
- content: item.content || '',
- project: item.project || '',
- project_name: item.project_name || '',
- create_time: item.create_time || '',
- update_time: item.update_time || '',
- score: item.score || 0
- })) */
- this.gsprTableData = responseData.list; //filteredItems
- this.gsprTotal = responseData.total; // 使用API返回的总数
- this.hasExistingData = this.gsprTableData.length > 0;
-
- // 保存原始数据用于后续对比
- this.gsprTableData.forEach(item => {
- if (!this.originalData.has(item.id)) {
- this.saveOriginalData(item);
- }
- });
- } catch (error) {
- console.error("数据加载失败:", error);
- this.$message.error(error.message || "获取数据失败");
- this.gsprTableData = [];
- this.gsprTotal = 0;
- this.hasExistingData = false;
- } finally {
- loading.close();
- }
- },
- // 添加一个辅助方法来检查位置是否匹配
- isLocationMatched(location) {
- if (!location) return false;
- const cleanLocation = location.replace(/[\[\]【】]/g, "");
- return !!this.matchedContents[cleanLocation];
- },
- handleGsprSearch() {
- this.currentPage = Number(
- localStorage.getItem(`gspr_page_${this.selectedProjectId}`)
- ) ; // 搜索时重置到第一页
- console.log(this.currentPage);
- this.searchList();
- /* this.fetchGsprData(); */
- },
- /* 搜索 */
- async searchList() {
- this.gsprTableData = [];
- const loading = this.$loading({
- lock: true,
- text: "加载中...",
- spinner: "el-icon-loading",
- background: "rgba(0, 0, 0, 0.7)",
- });
- try {
- const params = {
- project_id: this.selectedProjectId,
- page: this.currentPage,
- page_size: this.pageSize,
- sort_field: this.sort_field,
- sort_order: this.sort_order,
- ...this.gsprPageForm,
- ...this.gsprSearchForm,
- start_date: this.gsprSearchForm.dateRange?.[0] || null,
- end_date: this.gsprSearchForm.dateRange?.[1] || null,
- };
- const response = await projectList(params);
- response.data.list.map((el) => {
- if (el.score == null) {
- el.score = 0;
- }
- });
- this.gsprTableData = response.data.list; //filteredItems
- this.gsprTotal = response.data.total; // 使用API返回的总数
- /* this.currentPage = response.data.current_page; */
- this.hasExistingData = this.gsprTableData.length > 0;
-
- // 保存原始数据用于后续对比
- this.gsprTableData.forEach(item => {
- if (!this.originalData.has(item.id)) {
- this.saveOriginalData(item);
- }
- });
-
- console.log(response);
- } finally {
- loading.close();
- }
- },
- handleGsprReset() {
- this.gsprSearchForm = {
- keyword: "",
- department: "",
- project_id: this.selectedProjectId,
- dateRange: [],
- content_empty: null,
- };
- this.handleGsprSearch();
- },
- // 排序处理
- handleSortChange({ prop, order }) {
- this.sort_field = prop;
- this.sort_order = order === "ascending" ? "asc" : "desc";
- if (!order) {
- this.sort_field = "name";
- this.sort_order = "desc";
- }
- this.fetchGsprData();
- },
- /* 获取字段 */
- init() {
- departments().then((res) => {
- if (res.status !== 200) return;
- this.departList = res.data.departments;
- });
- /* 获取当前项目绑定的文档 */
- getProjectInfo({ id: this.$route.query.id }).then((res) => {
- this.documentIds = res.data.documentIds;
- });
- },
- handleGsprSizeChange(val) {
- this.pageSize = val;
- // this.currentPage = 1; // 切换每页显示数量时重置到第一页
- try {
- localStorage.setItem(
- `gspr_page_size_${this.selectedProjectId}`,
- String(this.pageSize)
- );
- localStorage.setItem(
- `gspr_page_${this.selectedProjectId}`,
- String(this.currentPage)
- );
- } catch (e) {
- // 忽略本地存储异常
- }
- this.fetchGsprData();
- },
- handleGsprCurrentChange(val) {
- this.currentPage = val;
- try {
- localStorage.setItem(
- `gspr_page_${this.selectedProjectId}`,
- String(this.currentPage)
- );
- } catch (e) {
- // 忽略本地存储异常
- }
- this.fetchGsprData();
- },
- // 获取行样式类名
- getRowClassName({ row }) {
- if (this.currentModifiedItem === row.id) {
- return 'modified-row';
- }
- return '';
- },
- // 标记项为已修改(只保持一个高亮)
- markAsModified(itemId) {
- // 清除之前的定时器
- if (this.clearTimer) {
- clearTimeout(this.clearTimer);
- this.clearTimer = null;
- }
-
- // 设置新的高亮项
- this.currentModifiedItem = itemId;
-
- // 设置10秒后自动清除
- this.clearTimer = setTimeout(() => {
- this.currentModifiedItem = null;
- this.clearTimer = null;
- }, 10000); // 10秒
- },
- // 清除修改标记
- clearModifiedMark() {
- if (this.clearTimer) {
- clearTimeout(this.clearTimer);
- this.clearTimer = null;
- }
- this.currentModifiedItem = null;
- },
- // 检查数据是否有变化
- checkDataChanged(newData, originalData) {
- if (!originalData) return true;
-
- const fieldsToCheck = ['name', 'tech_report_location', 'department', 'content', 'remarks', 'score'];
- return fieldsToCheck.some(field => newData[field] !== originalData[field]);
- },
- // 保存原始数据
- saveOriginalData(item) {
- this.originalData.set(item.id, { ...item });
- },
- },
-
- // 组件销毁时清理定时器
- beforeDestroy() {
- if (this.clearTimer) {
- clearTimeout(this.clearTimer);
- this.clearTimer = null;
- }
- },
- };
- </script>
- <style lang="scss" >
- .project-input-container {
- padding: 20px;
- }
- .editor-area {
- ::v-deep .cke_top {
- display: block; // Hide the original toolbar
- }
- }
- .project-input-container {
- padding: 20px;
- }
- .pagination-container {
- display: flex;
- justify-content: flex-end;
- }
- /* 添加编辑器工具栏样式 */
- .editor-area {
- .cke_top {
- display: flex !important;
- justify-content: space-between;
- align-items: center;
- padding: 6px 8px !important;
- /* 工具栏组样式 */
- .cke_toolgroup {
- margin: 0 2px;
- display: flex;
- align-items: center;
- }
- /* 工具栏按钮样式 */
- .cke_toolbar {
- float: none !important;
- display: inline-flex;
- align-items: center;
- }
- }
- }
- /* 如果需要调整编辑器容器宽度 */
- .cke_chrome {
- width: 100% !important;
- }
- /* 内容部分样式 */
- .content-cell {
- display: flex;
- align-items: center;
- justify-content: space-between;
- .content-text {
- flex: 1;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 430px; // 可以根据需要调整最大宽度
- }
- }
- /* 自定义 Tooltip 样式 */
- .custom-tooltip {
- min-width: 200px !important; // 设置最大宽度
- /* background-color: rgba(232, 232, 232, 0.9) !important; */
- line-height: 1.5; // 设置行高
- word-break: break-all; // 允许在任意字符间断行
- white-space: pre-wrap; // 保留空格和换行
- .el-tooltip__popper {
- padding: 10px !important; // 增加内边距
- font-size: 12px; // 设置字体大小
- }
- }
- ::v-deep .el-dialog {
- margin-top: 0 !important;
- }
- /* 修改项高亮样式 */
- .modified-row {
- background-color: #e6f7ff !important; /* 浅蓝色背景 */
- border-left: 4px solid #1890ff !important; /* 左侧蓝色边框 */
- transition: all 0.3s ease; /* 添加过渡效果 */
- }
- .modified-row:hover {
- background-color: #bae7ff !important; /* 悬停时更深的蓝色 */
- }
- @keyframes pulse {
- 0% {
- opacity: 1;
- transform: scale(1);
- }
- 50% {
- opacity: 0.7;
- transform: scale(1.1);
- }
- 100% {
- opacity: 1;
- transform: scale(1);
- }
- }
- @keyframes fadeOut {
- 0% {
- opacity: 1;
- }
- 90% {
- opacity: 1;
- }
- 100% {
- opacity: 0;
- }
- }
- </style>
|