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

feat:update数据资源库

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