提交 3b1415a3 authored 作者: coderBryanFu's avatar coderBryanFu

feat:update数据资源库

流水线 #102 已取消 于阶段
---
description:
alwaysApply: true
---
# Overview
Insert overview text here. The agent will only see this should they choose to apply the rule.
......
......@@ -2,10 +2,13 @@ stages:
- build
- deploy
# cache:
# key: "$CI_COMMIT_REF_SLUG"
# paths:
# - .npm/
cache:
# cache:key 这里使用字符串,兼容性更好(部分 linter 不支持 key: { files: [...] })
# 预分支 pre 需要快速构建并实时同步,因此让 .npm 下载缓存跨分支复用
key: "npm-cache-global"
paths:
- .npm/
policy: pull-push
build_pre:
stage: build
......@@ -17,8 +20,7 @@ build_pre:
script:
- node -v
- npm -v
- npm config set cache .npm --global
- npm ci --prefer-offline --no-audit --no-fund
- npm ci --cache "$CI_PROJECT_DIR/.npm" --prefer-offline --no-audit --no-fund
- npm run build
artifacts:
paths:
......@@ -35,5 +37,49 @@ deploy_pre:
dependencies:
- build_pre
script:
- apk add --no-cache rsync
- rsync -av --delete dist/ /nas/kjb_service/zm/pre-project/html/
\ No newline at end of file
- apk add --no-cache rsync curl jq
# 只允许“最新一次 pre pipeline”部署到 nginx(加二次确认,避免短时间多次推送导致重复 rsync)
- >
LATEST_PIPELINE_ID="$(
curl --silent --show-error --fail
--header "JOB-TOKEN: $CI_JOB_TOKEN"
"$CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/pipelines?ref=pre&order_by=id&sort=desc&per_page=1"
| jq -r '.[0].id'
)"
- >
if [ -z "$LATEST_PIPELINE_ID" ] || [ "$LATEST_PIPELINE_ID" != "$CI_PIPELINE_ID" ]; then
echo "skip deploy: not latest pipeline (latest=$LATEST_PIPELINE_ID current=$CI_PIPELINE_ID)";
exit 0;
fi
- sleep 20
- >
LATEST_PIPELINE_ID="$(
curl --silent --show-error --fail
--header "JOB-TOKEN: $CI_JOB_TOKEN"
"$CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/pipelines?ref=pre&order_by=id&sort=desc&per_page=1"
| jq -r '.[0].id'
)"
- >
if [ -z "$LATEST_PIPELINE_ID" ] || [ "$LATEST_PIPELINE_ID" != "$CI_PIPELINE_ID" ]; then
echo "skip deploy: not latest pipeline after debounce (latest=$LATEST_PIPELINE_ID current=$CI_PIPELINE_ID)";
exit 0;
fi
- rsync -avz --delete dist/ /nas/kjb_service/zm/pre-project/html/
# 非 protected 分支:push 时先做 build 校验(避免合并 pre 时出现 build 报错)
build_check:
stage: build
image: node:20-bullseye
tags:
- risk-monitor-frontend
# 只在 push 时做构建校验,且排除 protected 分支与目标分支 pre
only:
- pushes
except:
- pre
- protected_branches
script:
- node -v
- npm -v
- npm ci --cache "$CI_PROJECT_DIR/.npm" --prefer-offline --no-audit --no-fund
- npm run build
\ No newline at end of file
差异被折叠。
......@@ -36,7 +36,7 @@
"json5": "^2.2.3",
"lodash": "^4.17.21",
"markdown-it": "^14.1.0",
"pdfjs-dist": "^5.5.207",
"pdfjs-dist": "^3.11.174",
"pinia": "^3.0.4",
"vue": "^3.4.0",
"vue-router": "^4.2.5"
......
......@@ -3,9 +3,9 @@ import request from "@/api/request.js";
// 最新科技政令
export function getDepartmentList(params) {
return request({
method: 'GET',
method: 'POST',
url: `/api/administrativeDict/department`,
params
data: params
})
}
......@@ -99,15 +99,6 @@ export function getKeyOrganization(params) {
})
}
// 所有机构
export function getAllOrganization(params) {
return request({
method: 'POST',
url: `/api/administrativeOrderOverview/orderCount`,
data: params
})
}
// AI智能总结
export function getChartInterpretation(params) {
return request({
......
......@@ -33,7 +33,7 @@ export function getDecreeEntities(params) {
export function getDecreeRelatedChain(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderInfo/relatedChain/${params.id}`,
url: `/api/chain/relatedChain/${params.id}`,
})
}
......@@ -41,7 +41,7 @@ export function getDecreeRelatedChain(params) {
export function getDecreeChainNodes(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderInfo/relatedChainNodes/${params.id}`,
url: `/api/chain/relatedChainNodes/${params.id}`,
})
}
......@@ -49,7 +49,8 @@ export function getDecreeChainNodes(params) {
export function getDecreeRelatedEntitie(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderInfo/listRelatedEntitie/${params.id}`,
url: `/api/organization/shareholding`,
params
})
}
......
......@@ -30,12 +30,12 @@
<div class="organization-list" ref="refOrganization" v-loading="organizationInfo.loading">
<div class="organization-item" v-for="(item, index) in organizationInfo.list" :key="index" @click="handleToInstitution(item)">
<div class="item-left">
<img :src="item.imgUrl || DefaultIcon2" alt="" />
<img :src="item.orgImage || DefaultIcon2" alt="" />
</div>
<div class="item-right one-line-ellipsis">{{ item.orgName }}</div>
<div class="item-total">{{ item.totalOrderNum }}项</div>
<div class="item-total">{{ item.total }}项</div>
<el-icon color="var(--color-primary-100)"><ArrowRightBold /></el-icon>
<div class="item-dot" v-if="item.recentOrderNum">+{{item.recentOrderNum}}</div>
<div class="item-dot" v-if="item.totalRecent">+{{item.totalRecent}}</div>
</div>
</div>
<div class="pagination-box">
......@@ -56,7 +56,7 @@ import { Search } from '@element-plus/icons-vue'
import router from "@/router";
import TimeTabPane from '@/components/base/TimeTabPane/index.vue';
import { getAllOrganization } from "@/api/decree/home";
import { getDepartmentList } from "@/api/decree/home";
import tipsTcon from "./assets/icons/tips-icon.png";
import DefaultIcon2 from "@/assets/icons/default-icon2.png";
......@@ -77,7 +77,7 @@ const onAllOrganization = async (num) => {
organizationInfo.loading = true
try {
let {keyWord, pageNum, pageSize, day} = organizationInfo
const res = await getAllOrganization({day, pageNum:pageNum-1, pageSize, keyWord: keyWord||undefined});
const res = await getDepartmentList({day, pageNum:pageNum-1, pageSize, keyWord: keyWord||undefined});
console.log("机构列表", res);
if (res.code === 200) {
organizationInfo.list = res.data.orgList || [];
......
......@@ -47,7 +47,7 @@
<div class="item-dot" v-if="item.recentOrderNum">+{{item.recentOrderNum}}</div>
</div>
<div class="organization-item" @click="onNavigateTo()">
<div class="item-more">查看全部机构 ({{govInsList.length+1}}家)</div>
<div class="item-more">查看全部机构 ({{govInsList.length}}家)</div>
<el-icon color="var(--color-primary-100)"><ArrowRightBold /></el-icon>
</div>
</div>
......@@ -233,7 +233,7 @@
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">科技政领域分布情况,数据来源:美国各行政机构官网</div>
<div class="data-origin-text">科技政领域分布情况,数据来源:美国各行政机构官网</div>
</div>
<div class="ai-pane">
<AiButton />
......@@ -287,7 +287,7 @@
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">关键科技政列表,数据来源:美国各行政机构官网</div>
<div class="data-origin-text">关键科技政列表,数据来源:美国各行政机构官网</div>
</div>
</div>
<div class="box8">
......@@ -321,7 +321,7 @@
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">科技政重点条款词云,数据来源:美国各行政机构官网</div>
<div class="data-origin-text">科技政重点条款词云,数据来源:美国各行政机构官网</div>
</div>
</div>
</div>
......@@ -502,10 +502,10 @@ const govInsList = ref([]);
const checkedGovIns = ref([]);
const handleGetDepartmentList = async () => {
try {
const res = await getDepartmentList({day:7});
const res = await getDepartmentList({});
console.log("机构列表", res);
if (res.code === 200 && res.data) {
govInsList.value = res.data;
if (res.code === 200) {
govInsList.value = res.data.orgList;
}
} catch (error) {
console.error("获取机构列表error", error);
......
......@@ -100,7 +100,7 @@ import {
getDecreeRelatedEntitie
} from "@/api/decree/influence";
import ChartChain from "./com/ChartChain.vue";
import AiTips from "./com/AiTips.vue";
// import AiTips from "./com/AiTips.vue";
import GraphChart from "@/components/base/GraphChart/index.vue";
import defaultIcon2 from "@/assets/icons/default-icon2.png";
import noticeIcon from "./assets/images/notice-icon.png";
......@@ -186,7 +186,7 @@ const headerChartData = (row) => {
onDecreeRelatedChain(row.id)
break;
case 2:
onDecreeRelatedEntitie(row.id)
onDecreeRelatedEntitie(row.orgId)
break;
}
}
......@@ -247,14 +247,17 @@ const graphInfo = reactive({
nodes: [],
links: [],
});
const onDecreeRelatedEntitie = async (id) => {
const onDecreeRelatedEntitie = async (orgId) => {
try {
const res = await getDecreeRelatedEntitie({ id });
const res = await getDecreeRelatedEntitie({ orgId, rule:false, withSanInfo:false });
console.log("实体关系:", res);
if (res.code === 200) {
graphInfo.links = res.data.map(onFormatLink)
graphInfo.nodes = res.data.map(onFormatNode)
graphInfo.nodes.unshift(onFormatNode(entityInfo.node))
let arr1 = res.data.parentOrgList.map(item => ({ ...item, level: 1 }))
let arr3 = res.data.childrenOrgList.map(item => ({ ...item, level: 3 }))
graphInfo.links = [...arr1,...arr3].map(onFormatLink)
graphInfo.nodes = [...arr1,...arr3].map(onFormatNode)
graphInfo.nodes.unshift(onFormatNode({name:res.data.orgName, id:res.data.orgId}, -1))
}
} catch (error) {
console.log("获取实体关系失败", error);
......@@ -262,17 +265,18 @@ const onDecreeRelatedEntitie = async (id) => {
}
const onFormatLink = (item, index) => {
return {
id: `link-${index+1}`,
source: item.id+'', target: entityInfo.id+'',
label: { show: true, color: "#055fc2", backgroundColor: "#eef7ff", borderWidth: 0, offset: [0, 15], formatter: item.relation },
id: `link-${index}-${item.id}`,
target: item.level==3 ? `${index}-${item.id}` : `-1-${entityInfo.node.orgId}`,
source: item.level==3 ? `-1-${entityInfo.node.orgId}` : `${index}-${item.id}`,
label: { show: true, color: "#055fc2", backgroundColor: "#eef7ff", borderWidth: 0, offset: [0, 15], formatter: item.description },
lineStyle: { color: '#B9DCFF', type: "solid", opacity: 1 }
}
}
const onFormatNode = (item) => {
let leader = item.id == entityInfo.id;
const onFormatNode = (item, index) => {
let leader = item.id == entityInfo.node.orgId;
return {
id: item.id+'',
name: onWordWrap(item.companyName, 7),
id: `${index}-${item.id}`,
name: onWordWrap(item.name, 7),
label: {
show: true,
color: "#3b414b",
......
......@@ -11,15 +11,13 @@
</template>
<script>
import { ref, shallowRef, onMounted, nextTick, watch } from 'vue';
import * as pdfjsLib from 'pdfjs-dist';
import { TextLayer } from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.mjs',
import.meta.url
).href;
import { ref, shallowRef, nextTick, watch } from 'vue';
// 使用 legacy 入口,避免线上对 .mjs 的 MIME 配置导致 worker 动态 import 失败
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf';
import pdfWorkerUrl from 'pdfjs-dist/legacy/build/pdf.worker.min?url';
// 通过 Vite 的 ?url 产出静态资源地址,确保线上/线下都能正确加载 worker
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorkerUrl;
export default {
name: 'PdfViewer',
props: {
......@@ -50,6 +48,8 @@ export default {
const searchKey = ref('');
const matchList = ref([]);
const matchIdx = ref(0);
// pdfjs 3.x 的 renderTextLayer 在不同入口下导出不一致,这里做一次缓存 + 兜底加载
const pdfjsApiRef = shallowRef(pdfjsLib);
// 保存 canvas
const setCanvasRef = (page, el) => {
......@@ -115,15 +115,30 @@ export default {
await pdfPage.render({ canvasContext: context, viewport }).promise;
// 渲染 textLayer:pdfjs-dist v5 推荐用 TextLayer,renderTextLayer 可能不存在
// 渲染 textLayer(pdfjs-dist 3.x):使用 renderTextLayer(不要用 TextLayer 构造器)
try {
const textContent = await pdfPage.getTextContent();
const layer = new TextLayer({
textContentSource: textContent,
container: textLayer,
viewport
});
await layer.render();
let api = pdfjsApiRef.value || pdfjsLib;
let rt = api?.renderTextLayer;
// 兜底:某些入口下 renderTextLayer 不在 pdfjsLib 上,尝试 legacy 入口
if (typeof rt !== 'function') {
try {
const legacy = await import('pdfjs-dist/legacy/build/pdf');
pdfjsApiRef.value = legacy;
api = legacy;
rt = legacy?.renderTextLayer;
} catch (_) { }
}
if (typeof rt === 'function') {
await rt({
textContent,
container: textLayer,
viewport,
// pdfjs 3.x 需要传入 textDivs 数组
textDivs: [],
enhanceTextSelection: false
}).promise;
}
} catch (e) {
console.warn('textLayer 渲染失败', e);
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论