@ -0,0 +1,26 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org |
||||
|
||||
# top-most EditorConfig file |
||||
root = true |
||||
|
||||
# Unix-style newlines with a newline ending every file |
||||
[*] |
||||
end_of_line = lf |
||||
insert_final_newline = true |
||||
|
||||
# Matches multiple files with brace expansion notation |
||||
# Set default charset |
||||
[*.{js,jsx,ts,tsx,md}] |
||||
charset = utf-8 |
||||
indent_style = space |
||||
indent_size = 2 |
||||
tab_width = 2 |
||||
end_of_line = lf |
||||
insert_final_newline = true |
||||
trim_trailing_whitespace = true |
||||
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml |
||||
[{package.json,.travis.yml}] |
||||
indent_style = space |
||||
indent_size = 2 |
||||
@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files. |
||||
|
||||
# dependencies |
||||
/node_modules |
||||
|
||||
# testing |
||||
/coverage |
||||
|
||||
# production |
||||
/build |
||||
/package |
||||
|
||||
# misc |
||||
.DS_Store |
||||
.env.local |
||||
.env.development.local |
||||
.env.test.local |
||||
.env.production.local |
||||
.history |
||||
*.log |
||||
|
||||
# secrets |
||||
secrets.*.js |
||||
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件 |
||||
/shelf/ |
||||
/workspace.xml |
||||
# 基于编辑器的 HTTP 客户端请求 |
||||
/httpRequests/ |
||||
# Datasource local storage ignored files |
||||
/dataSources/ |
||||
/dataSources.local.xml |
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="ProjectModuleManager"> |
||||
<modules> |
||||
<module fileurl="file://$PROJECT_DIR$/my-crx.iml" filepath="$PROJECT_DIR$/my-crx.iml" /> |
||||
</modules> |
||||
</component> |
||||
</project> |
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="VcsDirectoryMappings"> |
||||
<mapping directory="" vcs="Git" /> |
||||
</component> |
||||
</project> |
||||
@ -0,0 +1,26 @@
|
||||
# OS |
||||
.DS_Store |
||||
|
||||
# ignore node dependency directories & lock |
||||
node_modules |
||||
yarn.lock |
||||
pnpm-lock.yaml |
||||
package-lock.json |
||||
|
||||
# ignore log files and local |
||||
*.log |
||||
*.local |
||||
.env.local |
||||
.env.development.local |
||||
.env.test.local |
||||
.env.production.local |
||||
.history |
||||
|
||||
# ignore compiled files |
||||
build |
||||
types |
||||
coverage |
||||
|
||||
# ignore ide settings |
||||
.idea |
||||
.vscode |
||||
@ -0,0 +1,6 @@
|
||||
# Ignore artifacts: |
||||
build |
||||
coverage |
||||
node_modules |
||||
pnpm-lock.yaml |
||||
pnpm-workspace.yaml |
||||
@ -0,0 +1,10 @@
|
||||
{ |
||||
"jsxSingleQuote": false, |
||||
"singleQuote": true, |
||||
"trailingComma": "all", |
||||
"endOfLine": "lf", |
||||
"printWidth": 100, |
||||
"semi": false, |
||||
"tabWidth": 2, |
||||
"useTabs": false |
||||
} |
||||
@ -0,0 +1,3 @@
|
||||
{ |
||||
"recommendations": ["Vue.volar"] |
||||
} |
||||
@ -0,0 +1,15 @@
|
||||
# CHANGELOG |
||||
|
||||
```txt |
||||
Summary |
||||
1. document grouping follow 'SemVer2.0' protocol |
||||
2. use 'PATCH' as a minimum granularity |
||||
3. use concise descriptions |
||||
4. type: feat \ fix \ update \ perf \ remove \ docs \ chore |
||||
5. version timestamp follow the yyyy.MM.dd format |
||||
``` |
||||
|
||||
## 0.0.0 [2024.12.19] |
||||
|
||||
- feat: initial |
||||
- feat: generator by  |
||||
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT) |
||||
|
||||
Copyright (c) 2024-present, no one |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in |
||||
all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
THE SOFTWARE. |
||||
@ -0,0 +1,44 @@
|
||||
# my-crx |
||||
|
||||
> a chrome extension tools built with Vite + Vue, and Manifest v3 |
||||
|
||||
## Installing |
||||
|
||||
1. Check if your `Node.js` version is >= **14**. |
||||
2. Change or configurate the name of your extension on `src/manifest`. |
||||
3. Run `npm install` to install the dependencies. |
||||
|
||||
## Developing |
||||
|
||||
run the command |
||||
|
||||
```shell |
||||
$ cd my-crx |
||||
|
||||
$ npm run dev |
||||
``` |
||||
|
||||
### Chrome Extension Developer Mode |
||||
|
||||
1. set your Chrome browser 'Developer mode' up |
||||
2. click 'Load unpacked', and select `my-crx/build` folder |
||||
|
||||
### Nomal FrontEnd Developer Mode |
||||
|
||||
1. access `http://0.0.0.0:3000/` |
||||
2. when debugging popup page, open `http://0.0.0.0:3000//popup.html` |
||||
3. when debugging options page, open `http://0.0.0.0:3000//options.html` |
||||
|
||||
## Packing |
||||
|
||||
After the development of your extension run the command |
||||
|
||||
```shell |
||||
$ npm run build |
||||
``` |
||||
|
||||
Now, the content of `build` folder will be the extension ready to be submitted to the Chrome Web Store. Just take a look at the [official guide](https://developer.chrome.com/webstore/publish) to more infos about publishing. |
||||
|
||||
--- |
||||
|
||||
Generated by [create-chrome-ext](https://github.com/guocaoyi/create-chrome-ext) |
||||
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY----- |
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDj8i8RxTfm5x/H |
||||
nWGgwQLuN2WOFLL4qseldExeSmSIwPIdCDf0s50u74GMp7UhCfTAIRNFVj26FHts |
||||
Re6lpu8d/lgmUVg+x5xqZ0nsix64JQZkvCUnh0B3ljgM0ohKq4t/h1gTRdARWdIx |
||||
SES9oyKv9FwmSOZirjlZGhDYA90rYJJaRKrsAkLL/BOi1QBfOecGi1KKtPfZgxZm |
||||
g0EiY4o2dKl7uD5nAg8T8HBqkNqAOXcDvuI4Xw4sLTXSVBFqDr7FY/eDwSlBDS91 |
||||
v5xaMte8In4tEW1cvqIwiaEgcB3+rsPWHm/xcE902MtfCGZckAdVk/uJi4NYI2HB |
||||
nFYZ+4l7AgMBAAECggEARr8xfbX0yj0cv3MozGBrXO0jn2vsOkKjEUadtzwmpM0h |
||||
uAIHf/LJS7p5PmME1BrHdGQvm3sPUHQv9GRmE9LSb0A0MrxXg7Cnatzg9EIfiC6x |
||||
o81DSNfxGcytW8lyFJ7Wfn/OxbiKwf/bPYF9GFCMIQlaGmXQ33rMgMuWw7y7Rc0u |
||||
0OZ1RI1IiZqrcgDDjOTos5Jrvutl31A18j0hhlwpEQ/t6k1r7zXCBp1JulKMNG8s |
||||
to8cvYIUAF4xAvP0FyF/xOhZ6zSplS4tAYbRfR4TdA2bO67IRDONtU4KKc3vNXm6 |
||||
FPEnvu3AxRcv5Z1yh3BQ6gd912wweSyzZgPyB9SRFQKBgQD3b9N1jZVoUB45Tv9A |
||||
/KwQzqyVy5T2ZGKMTiygVeNjJzYUH0vQejtuVf7DRDK2NSQDIJEswkVbNqFM2lB4 |
||||
6DO7lvJl2VLW2WA4J0UOdzzElzmj8lGSpacJNMn07ARCr6xsa2ohjMU6FRIs9eih |
||||
mHDzXJ5jFJ90dc12AnEU9isdnQKBgQDr1a25jn7psFcXLWOjb7aDPaObBNKufWBB |
||||
+SMsofb86db4wpvwAmOze9VkVXDxyxUJFpmIhcDGRC+e0xAS7XTBMHfqFggXN15N |
||||
ippbsgvFjTG1J5MrW8QftyQLVGTlh4UWbAd1MAwhKaHxsv3fma2QGedYBNVN8Lmn |
||||
0z0mmXOj9wKBgQDE8llZw+wcL0kfTGWXkIZUimyMfpJ+28Ak9URICpUHIvrEYXSy |
||||
59fOJty1B99lcD7Nzmo/OhKCW6VfypUq54fl1Gvz7Vsmb7dKetdYgEf6InRarlGH |
||||
bBrcFQx6yjFJ8xJG6eh789Q4OvVQnBsiJHgi/KGFM7EHvYxgVek0SgxfcQKBgQDi |
||||
bosaeiKIpXM5Ia9lIMGQSQJouLzAJEjjjx2ioO/P7YUl21R1oRWxItjEOPT0cxSD |
||||
YVuQpTtuIdHHIMdyJOPRCYRZEUY7ZEH7GXUTTWPYDbUIRjMbkkRAX0sQRbuKVhAG |
||||
czrF3ZgHzz1aH415vsfSb8ybyigzaUoYRM2V3ggWCQKBgA1LvK/iRNC1VYFWBjNG |
||||
8kYD6SsyLUffrvZzRdTPx21nCuTJGnVLi2mUEM+YKJEHGLUvNmqFRdNwlOPAslmG |
||||
4VZFMStllrQb5aPMxzkYW8y2DOZx7OfDCpCvxrK7O/zdpoZCwzFbsNTRm1iY4JKy |
||||
vDtr/sYmttfmrjEBL7LG8+VG |
||||
-----END PRIVATE KEY----- |
||||
@ -0,0 +1,14 @@
|
||||
<!doctype html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<link rel="icon" href="/icons/logo.ico" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>Chrome Extension + Vue + TS + Vite</title> |
||||
</head> |
||||
|
||||
<body> |
||||
<div id="app"></div> |
||||
<script type="module" src="./src/devtools/index.ts"></script> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,232 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="zh-CN"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<title>数字督查插件下载</title> |
||||
<style> |
||||
body { |
||||
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; |
||||
background-color: #f9f9f9; |
||||
color: #000; |
||||
margin: 0; |
||||
padding: 0; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
height: 100vh; |
||||
text-align: center; |
||||
} |
||||
|
||||
.container { |
||||
background: #fff; |
||||
padding: 3rem; |
||||
border-radius: 18px; |
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); |
||||
max-width: 500px; |
||||
width: 100%; |
||||
} |
||||
|
||||
h1 { |
||||
font-size: 2.5rem; |
||||
margin-bottom: 2rem; |
||||
color: #007aff; |
||||
font-weight: 600; |
||||
} |
||||
|
||||
@keyframes smooth-gradient { |
||||
0% { |
||||
background-position: 0% 50%; |
||||
} |
||||
100% { |
||||
background-position: 100% 50%; |
||||
} |
||||
} |
||||
|
||||
#downloadBtn { |
||||
background-color: #007aff; |
||||
color: white; |
||||
border: none; |
||||
padding: 1.2rem 2.5rem; |
||||
font-size: 1.1rem; |
||||
border-radius: 24px; |
||||
cursor: pointer; |
||||
transition: background-color 0.3s ease; |
||||
box-shadow: 0 4px 12px rgba(0, 122, 255, 0.2); |
||||
display: inline-flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
#downloadBtn:hover { |
||||
background: linear-gradient(270deg, #fe0000, #ff5d00, #ffff00, #fe0000); |
||||
background-size: 400% 400%; |
||||
animation: smooth-gradient 3s ease infinite; |
||||
} |
||||
|
||||
#downloadBtn:active, #downloadBtn:focus { |
||||
animation: none; |
||||
background-color: #007aff; |
||||
} |
||||
|
||||
.info { |
||||
margin-top: 2rem; |
||||
font-size: 0.9rem; |
||||
color: #888; |
||||
} |
||||
|
||||
.info p { |
||||
margin: 0.7rem 0; |
||||
} |
||||
|
||||
.instructions { |
||||
margin-top: 3rem; |
||||
font-size: 0.9rem; |
||||
color: #555; |
||||
text-align: left; |
||||
line-height: 1.8; |
||||
} |
||||
|
||||
.instructions h2 { |
||||
font-size: 1.3rem; |
||||
color: #007aff; |
||||
margin-bottom: 1rem; |
||||
font-weight: 600; |
||||
} |
||||
|
||||
.instructions p { |
||||
position: relative; |
||||
padding-left: 2rem; |
||||
} |
||||
|
||||
.instructions p::before { |
||||
content: counter(step-counter); |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
color: white; |
||||
background-color: #007aff; |
||||
width: 20px; |
||||
height: 20px; |
||||
border-radius: 50%; |
||||
text-align: center; |
||||
line-height: 20px; |
||||
font-size: 0.9rem; |
||||
} |
||||
|
||||
.instructions { |
||||
counter-reset: step-counter; |
||||
} |
||||
|
||||
.instructions p { |
||||
counter-increment: step-counter; |
||||
} |
||||
|
||||
/* 新增样式 */ |
||||
.browser-nav { |
||||
display: flex; |
||||
justify-content: center; |
||||
gap: 10px; |
||||
margin-bottom: 1rem; |
||||
} |
||||
|
||||
.browser-btn { |
||||
padding: 0.5rem 1.5rem; |
||||
border: 1px solid #ddd; |
||||
border-radius: 8px; |
||||
cursor: pointer; |
||||
font-size: 0.9rem; |
||||
background-color: #fff; |
||||
transition: background-color 0.3s ease-out, color 0.3s ease-out; |
||||
} |
||||
|
||||
.browser-btn.active { |
||||
background-color: #007aff; |
||||
color: white; |
||||
border-color: #007aff; |
||||
} |
||||
|
||||
.install-content { |
||||
display: none; |
||||
} |
||||
|
||||
.install-content.active { |
||||
display: block; |
||||
} |
||||
</style> |
||||
</head> |
||||
<body> |
||||
<div class="container"> |
||||
<h1>欢迎使用数字督查插件</h1> |
||||
<button id="downloadBtn"> |
||||
<span class="download-logo"></span> 点此下载插件 |
||||
</button> |
||||
<div class="info"> |
||||
<p>版本号 : 1.0.1</p> |
||||
<p>更新时间 : 2025年2月20日</p> |
||||
<p>兼容内核 : Chromium 88及以上</p> |
||||
<p>©Copr. 长沙创客</p> |
||||
</div> |
||||
|
||||
<div class="instructions"> |
||||
<h2>插件安装方法</h2> |
||||
<div class="browser-nav"> |
||||
<button class="browser-btn active" data-browser="native">国产浏览器</button> |
||||
<button class="browser-btn" data-browser="chrome">Chrome浏览器</button> |
||||
<button class="browser-btn" data-browser="edge">Edge浏览器</button> |
||||
</div> |
||||
|
||||
<!-- 国产浏览器安装方法 --> |
||||
<div class="install-content active" id="native"> |
||||
<p>下载插件(crx 文件)后,找到下载的文件。</p> |
||||
<p>启动浏览器,将下载好的 crx 文件拖动至浏览器页面。</p> |
||||
<p>松开鼠标,3秒内系统将弹出“添加数字督察”提示框。</p> |
||||
<p>点击“添加”按钮,即可完成插件安装(或升级)操作。</p> |
||||
</div> |
||||
|
||||
<!-- Chrome浏览器安装方法 --> |
||||
<div class="install-content" id="chrome"> |
||||
<p>在浏览器地址栏输入 <b>chrome://extensions/</b> 并按下回车键,打开扩展程序页面。</p> |
||||
<p>打开右上方的 “开发者模式” 开关。</p> |
||||
<p>将下载的 crx 插件后缀名改为 <b>.zip</b>,并解压成文件夹。</p> |
||||
<p>点击浏览器左上方 “加载已解压的扩展程序” 按钮,选择刚才的文件夹。</p> |
||||
</div> |
||||
|
||||
<!-- Edge浏览器安装方法 --> |
||||
<div class="install-content" id="edge"> |
||||
<p>在浏览器地址栏输入 <b>edge://extensions/</b> 并按下回车键,打开扩展页面。</p> |
||||
<p>打开左侧的 “开发人员模式” 开关。</p> |
||||
<p>将下载的 crx 插件拖动到浏览器框内。</p> |
||||
<p>在弹出框里点击 “添加扩展” 即可完成插件安装(或升级)操作。</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<script> |
||||
document.getElementById('downloadBtn').addEventListener('click', () => { |
||||
window.location.href = 'http://65.47.6.108/extension/shuziducha.crx'; |
||||
}); |
||||
|
||||
// 浏览器按钮点击事件 |
||||
document.querySelectorAll('.browser-btn').forEach(button => { |
||||
button.addEventListener('click', () => { |
||||
// 移除所有按钮的active类 |
||||
document.querySelectorAll('.browser-btn').forEach(btn => { |
||||
btn.classList.remove('active'); |
||||
}); |
||||
|
||||
// 移除所有安装内容的active类 |
||||
document.querySelectorAll('.install-content').forEach(content => { |
||||
content.classList.remove('active'); |
||||
}); |
||||
|
||||
// 添加当前按钮的active类 |
||||
button.classList.add('active'); |
||||
|
||||
// 显示对应的内容 |
||||
const browserType = button.dataset.browser; |
||||
document.getElementById(browserType).classList.add('active'); |
||||
}); |
||||
}); |
||||
</script> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,46 @@
|
||||
// 后端接口
|
||||
// http://localhost:5172/api/v2
|
||||
// http://192.168.31.146:5172/api/v2
|
||||
// http://65.47.6.108/api/v2
|
||||
export const supervisionUrl = 'http://65.47.6.108/api/v2'; |
||||
// 检测更新接口
|
||||
// http://localhost:8080/version.json
|
||||
// http://65.47.6.108/extension/version.json
|
||||
export const checkUpdateUrl = 'http://65.47.6.108/extension/version.json'; |
||||
// 后端案件核查信息上传 POST
|
||||
export const supervisionAjhcInfoUrl = 'http://65.47.6.108/api/v2/crx/ajhc'; |
||||
// 后端案件核查ID列表上传 POST
|
||||
export const supervisionAjhcIdsUrl = 'http://65.47.6.108/api/v2/crx/ajhc/outerIds'; |
||||
// 后端案件核查附件上传 POST
|
||||
export const supervisionAjhcFileUploadUrl = 'http://65.47.6.108/api/v2/crx/ajhc/file'; |
||||
// 后端案件核查处置意见上传 POST
|
||||
export const supervisionAjhcDisposalOpinionUrl = 'http://65.47.6.108/api/v2/crx/ajhc/disposalOpinion'; |
||||
// 后端案件核查处置明细上传 POST
|
||||
export const supervisionAjhcDisposalDetailsUrl = 'http://65.47.6.108/api/v2/crx/ajhc/disposalDetails'; |
||||
// 后端案件核查检查是否提交过办结信息 POST(已弃用)
|
||||
export const supervisionAjhcCheckCompletionInformationUrl = 'http://65.47.6.108/api/v2/crx/ajhcCheckCompletionInformation'; |
||||
// 后端案件核查检查是否需要填表 GET
|
||||
export const supervisionAjhcCheckTrueSituationUrl = 'http://65.47.6.108/api/v2/crx/ajhc/trueSituation?outerId='; |
||||
// 案件核查,本地测试用
|
||||
export const ajhcSysUrl = 'http://localhost:5172'; |
||||
export const ajhcInfoUrl = '/#/sensitivePerception/modelClue'; |
||||
|
||||
// 插件界面更多按钮
|
||||
// 'http://localhost:5172/#/data/ajhc'
|
||||
// 'http://65.47.6.108/v2/#/data/Ajhc'
|
||||
export const morePageUrl = 'http://65.47.6.108/v2/#/data/Ajhc'; |
||||
|
||||
// 案件核查子系统
|
||||
export const ajhcOutSysUrl = 'http://11.33.3.4'; |
||||
// 案件核查子系统-投诉受理
|
||||
export const ajhcOutInfoUrl = 'http://11.33.3.4/complaintacceptance'; |
||||
// 案件核查子系统-在办案件
|
||||
export const ajhcInProgressUrl = 'http://11.33.3.4/workcenterlist/doingcases'; |
||||
|
||||
|
||||
// 案件核查投诉受理(已弃用)
|
||||
export const netComplaintUrl = 'http://11.33.3.4/complaintacceptance/netcomplaint'; |
||||
export const phoneComplaintUrl = 'http://11.33.3.4/complaintacceptance/phonecomplaint'; |
||||
export const letterComplaintUrl = 'http://11.33.3.4/complaintacceptance/lettercomplaint'; |
||||
export const otherComplaintUrl = 'http://11.33.3.4/complaintacceptance/othercomplaint'; |
||||
|
||||
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<module type="WEB_MODULE" version="4"> |
||||
<component name="NewModuleRootManager" inherit-compiler-output="true"> |
||||
<exclude-output /> |
||||
<content url="file://$MODULE_DIR$" /> |
||||
<orderEntry type="inheritedJdk" /> |
||||
<orderEntry type="sourceFolder" forTests="false" /> |
||||
</component> |
||||
</module> |
||||
@ -0,0 +1,14 @@
|
||||
<!doctype html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<link rel="icon" href="/icons/logo.ico" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>Chrome Extension + Vue + TS + Vite</title> |
||||
</head> |
||||
|
||||
<body> |
||||
<div id="app"></div> |
||||
<script type="module" src="./src/newtab/index.ts"></script> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,13 @@
|
||||
<!doctype html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<link rel="icon" href="/icons/logo.ico" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>Chrome Extension + Vue + TS + Vite</title> |
||||
</head> |
||||
<body> |
||||
<div id="app"></div> |
||||
<script type="module" src="/src/options/index.ts"></script> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,40 @@
|
||||
{ |
||||
"name": "my-crx", |
||||
"displayName": "数字督察", |
||||
"version": "1.0.1", |
||||
"author": "I", |
||||
"description": "数字督察插件", |
||||
"type": "module", |
||||
"license": "MIT", |
||||
"keywords": [ |
||||
"chrome-extension", |
||||
"vue", |
||||
"vite", |
||||
"create-chrome-ext" |
||||
], |
||||
"engines": { |
||||
"node": ">=14.18.0" |
||||
}, |
||||
"scripts": { |
||||
"dev": "vite", |
||||
"build": "vue-tsc --noEmit && vite build", |
||||
"preview": "vite preview", |
||||
"fmt": "prettier --write '**/*.{vue,ts,json,css,scss,md}'", |
||||
"zip": "npm run build && node src/zip.js" |
||||
}, |
||||
"dependencies": { |
||||
"vue": "^3.3.4" |
||||
}, |
||||
"devDependencies": { |
||||
"@crxjs/vite-plugin": "^2.0.0-beta.19", |
||||
"@types/chrome": "^0.0.246", |
||||
"@vitejs/plugin-vue": "^4.4.0", |
||||
"element-plus": "^2.8.8", |
||||
"gulp": "^4.0.2", |
||||
"gulp-zip": "^6.0.0", |
||||
"prettier": "^3.0.3", |
||||
"typescript": "5.6.2", |
||||
"vite": "^4.4.11", |
||||
"vue-tsc": "2.0.29" |
||||
} |
||||
} |
||||
@ -0,0 +1,13 @@
|
||||
<!doctype html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<link rel="icon" href="/icon/logo.ico" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>Chrome Extension + Vue + TS + Vite</title> |
||||
</head> |
||||
<body> |
||||
<div id="app"></div> |
||||
<script type="module" src="/src/popup/index.ts"></script> |
||||
</body> |
||||
</html> |
||||
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 750 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 908 B |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
@ -0,0 +1,13 @@
|
||||
<!doctype html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<link rel="icon" href="/icons/logo.ico" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>Chrome Extension + Vue + TS + Vite</title> |
||||
</head> |
||||
<body> |
||||
<div id="app"></div> |
||||
<script type="module" src="/src/sidepanel/index.ts"></script> |
||||
</body> |
||||
</html> |
||||
|
After Width: | Height: | Size: 109 KiB |
@ -0,0 +1,316 @@
|
||||
import { |
||||
checkUpdateUrl, |
||||
supervisionAjhcCheckTrueSituationUrl, |
||||
supervisionAjhcDisposalDetailsUrl, |
||||
supervisionAjhcDisposalOpinionUrl, |
||||
supervisionAjhcFileUploadUrl, |
||||
supervisionAjhcIdsUrl, |
||||
supervisionAjhcInfoUrl |
||||
} from "../../globalConfig"; |
||||
import { ajax } from "../utils/ajax"; |
||||
|
||||
console.log('后台开始运行'); |
||||
|
||||
// 当前申请案件办结的outerId
|
||||
let curCompletionOuterId: string | null = null; |
||||
|
||||
chrome.webRequest.onBeforeRequest.addListener( |
||||
/* |
||||
案件核查-上传附件-核查报告 |
||||
*/ |
||||
(details) => { |
||||
// 检查是否是目标接口
|
||||
if (details.url === "http://11.33.3.4/api/v5/gzzx/ajhc/hcbg") { |
||||
// 获取请求体
|
||||
const requestBody = details.requestBody; |
||||
if (requestBody) { |
||||
// 检查请求体是否为JSON格式
|
||||
if (requestBody.raw && requestBody.raw.length > 0) { |
||||
const rawBytes = requestBody.raw[0].bytes; |
||||
// 检查 rawBytes 是否为 undefined
|
||||
if (rawBytes) { |
||||
const decoder = new TextDecoder('utf-8'); |
||||
const requestBodyString = decoder.decode(rawBytes); |
||||
try { |
||||
const requestBodyJson = JSON.parse(requestBodyString); |
||||
const fileUrl = requestBodyJson.wjUrl; |
||||
const fileName = requestBodyJson.wjMc; |
||||
const outerId = requestBodyJson.ajId; |
||||
const fileType = "核查报告"; |
||||
|
||||
// 发送wjUrl和ajId到指定接口
|
||||
ajax(supervisionAjhcFileUploadUrl, { |
||||
method: "POST", |
||||
body: { fileUrl, fileName, outerId, fileType } |
||||
}) |
||||
.then((data: any) => console.log("Success:", data)) |
||||
.catch((error: any) => console.error("Error:", error)); |
||||
} catch (error) { |
||||
console.error("上传核查报告Error parsing JSON:", error); |
||||
} |
||||
} else { |
||||
console.error("上传核查报告requestBody.raw[0].bytes is undefined"); |
||||
} |
||||
} |
||||
} |
||||
} else if (details.url === "http://11.33.3.4/api/v5/gzzx/ajhc/zjxx") { |
||||
/* |
||||
案件核查-上传附件-核查证据 |
||||
*/ |
||||
// 获取请求体
|
||||
const requestBody = details.requestBody; |
||||
if (requestBody) { |
||||
if (requestBody.raw && requestBody.raw.length > 0) { |
||||
const rawBytes = requestBody.raw[0].bytes; |
||||
if (rawBytes) { |
||||
const decoder = new TextDecoder('utf-8'); |
||||
const requestBodyString = decoder.decode(rawBytes); |
||||
try { |
||||
const requestBodyJson = JSON.parse(requestBodyString); |
||||
if (requestBodyJson.fj && requestBodyJson.fj.length > 0) { |
||||
const fileUrl = requestBodyJson.fj[0].wjUrl; |
||||
const fileName = requestBodyJson.fj[0].wjMc; |
||||
const outerId = requestBodyJson.ajId; |
||||
const fileType = "核查证据"; |
||||
// 发送wjUrl和ajId到指定接口
|
||||
ajax(supervisionAjhcFileUploadUrl, { |
||||
method: "POST", |
||||
body: { fileUrl, fileName, outerId, fileType } |
||||
}) |
||||
.then((data: any) => console.log("Success:", data)) |
||||
.catch((error: any) => console.error("Error:", error)); |
||||
} |
||||
} catch (error) { |
||||
console.error("上传核查证据Error parsing JSON:", error); |
||||
} |
||||
} else { |
||||
console.error("上传核查证据requestBody.raw[0].bytes is undefined"); |
||||
} |
||||
} |
||||
} |
||||
} else if (details.url === "http://11.33.3.4/api/v5/gzzx/ajcz") { |
||||
/* |
||||
案件核查-上传处置意见 |
||||
*/ |
||||
const requestBody = details.requestBody; |
||||
if (requestBody) { |
||||
if (requestBody.raw && requestBody.raw.length > 0) { |
||||
const rawBytes = requestBody.raw[0].bytes; |
||||
if (rawBytes) { |
||||
const decoder = new TextDecoder('utf-8'); |
||||
const requestBodyString = decoder.decode(rawBytes); |
||||
try { |
||||
const requestBodyJson = JSON.parse(requestBodyString); |
||||
const handlingSuggestions = requestBodyJson.cljg; |
||||
const outerId = requestBodyJson.ajId; |
||||
const trueSituation = requestBodyJson.ssqkMc; |
||||
const complaintNature = requestBodyJson.tsWtxz; |
||||
ajax(supervisionAjhcDisposalOpinionUrl, { |
||||
method: "POST", |
||||
body: { handlingSuggestions, outerId, trueSituation, complaintNature } |
||||
}) |
||||
.then((data: any) => console.log("Success:", data)) |
||||
.catch((error: any) => console.error("Error:", error)); |
||||
} catch (error) { |
||||
console.error("上传处置意见Error parsing JSON:", error); |
||||
} |
||||
} else { |
||||
console.error("上传处置意见requestBody.raw[0].bytes is undefined"); |
||||
} |
||||
} |
||||
} |
||||
} else if (details.url === "http://11.33.3.4/api/v5/gzzx/ajcz/czmx") { |
||||
/* |
||||
案件核查-上传处置明细 |
||||
*/ |
||||
const requestBody = details.requestBody; |
||||
if (requestBody) { |
||||
if (requestBody.raw && requestBody.raw.length > 0) { |
||||
const rawBytes = requestBody.raw[0].bytes; |
||||
if (rawBytes) { |
||||
const decoder = new TextDecoder('utf-8'); |
||||
const requestBodyString = decoder.decode(rawBytes); |
||||
try { |
||||
const requestBodyJson = JSON.parse(requestBodyString); |
||||
const outerId = requestBodyJson.ajId; |
||||
const disposalType = requestBodyJson.czlx; |
||||
const disposalDepart = requestBodyJson.czDw; |
||||
const disposalAlarm = requestBodyJson.czJh; |
||||
const disposalName = requestBodyJson.czMc; |
||||
const disposalIdNo = requestBodyJson.czSfz; |
||||
const disposalSituation = requestBodyJson.czqk; |
||||
const disposalFileUrl = requestBodyJson.flwsUrl; |
||||
const disposalFileName = requestBodyJson.flwsMc; |
||||
const disposalSituationDescription = requestBodyJson.qksm; |
||||
ajax(supervisionAjhcDisposalDetailsUrl, { |
||||
method: "POST", |
||||
body: { outerId, disposalType, disposalDepart, disposalAlarm, disposalName, disposalIdNo, disposalSituation, disposalFileUrl, disposalFileName, disposalSituationDescription } |
||||
}) |
||||
.then((data: any) => console.log("Success:", data)) |
||||
.catch((error: any) => console.error("Error:", error)); |
||||
} catch (error) { |
||||
console.error("上传处置明细Error parsing JSON:", error); |
||||
} |
||||
} else { |
||||
console.error("上传处置明细requestBody.raw[0].bytes is undefined"); |
||||
} |
||||
} |
||||
} |
||||
} else if (details.url.startsWith("http://11.33.3.4/api/v5/gzzx/bj/anqx/")) { |
||||
/* |
||||
案件核查-获取案件办结案件id |
||||
*/ |
||||
const url = new URL(details.url); |
||||
const pathSegments = url.pathname.split('/'); |
||||
curCompletionOuterId = pathSegments[pathSegments.length - 1]; |
||||
} |
||||
}, |
||||
{ urls: ["<all_urls>"] }, |
||||
["requestBody"] // 显式请求 requestBody
|
||||
); |
||||
|
||||
/* |
||||
案件核查-提供当前申请案件办结的outerId |
||||
*/ |
||||
// 监听来自 contentScript 的消息
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { |
||||
if (request.action === 'getCurCompletionOuterId') { |
||||
console.log('发送getCurCompletionOuterId:' + curCompletionOuterId); |
||||
sendResponse(curCompletionOuterId); |
||||
} |
||||
}); |
||||
|
||||
/* |
||||
案件核查-ID列表上传 |
||||
*/ |
||||
// 监听来自 contentScript 的消息
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { |
||||
if (request.action === 'fetchDataRowKeys') { |
||||
const dataRowKeyMaps = request.data; |
||||
// 使用 ajax 方法发送请求
|
||||
ajax(supervisionAjhcIdsUrl, { |
||||
method: 'POST', |
||||
body: dataRowKeyMaps |
||||
}) |
||||
.then((data: any) => { |
||||
console.log('ID列表上传Success:', data); |
||||
sendResponse({ status: 'success', data }); |
||||
}) |
||||
.catch((error: any) => { |
||||
console.error('ID列表上传Error:', error); |
||||
sendResponse({ status: 'error', error }); |
||||
}); |
||||
return true; |
||||
} |
||||
}); |
||||
|
||||
/* |
||||
案件核查-投诉受理数据上传 |
||||
*/ |
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { |
||||
if (request.action === 'sendAjhcData') { |
||||
const { caseNumber, caseSource, complaintTime, reporterName, reporterContact, verifiedObjectUnit, verifiedObjectUnitCode, verifiedObjectName, verifiedObjectPosition, briefCase, organization, complaintIssueNature, acceptanceLevel } = request.data; |
||||
|
||||
// 使用 ajax 方法发送请求
|
||||
ajax(supervisionAjhcInfoUrl, { |
||||
method: 'POST', |
||||
body: { caseNumber, caseSource, complaintTime, reporterName, reporterContact, verifiedObjectUnit, verifiedObjectUnitCode, verifiedObjectName, verifiedObjectPosition, briefCase, organization, complaintIssueNature, acceptanceLevel } |
||||
}) |
||||
.then(response => response.json()) |
||||
.then((data: any) => { |
||||
console.log('投诉受理数据上传Success:', data); |
||||
sendResponse({ status: 'success', data }); |
||||
}) |
||||
.catch((error: any) => { |
||||
console.error('投诉受理数据上传Error:', error); |
||||
sendResponse({ status: 'error', error }); |
||||
}); |
||||
return true; |
||||
} |
||||
}); |
||||
|
||||
/* |
||||
案件核查-检查是否需要填表 |
||||
*/ |
||||
// 监听来自 contentScript 的消息
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { |
||||
if (request.action === 'checkTrueSituation') { |
||||
// 检查 curCompletionOuterId 是否为 null
|
||||
if (curCompletionOuterId !== null) { |
||||
// 使用 ajax 方法发送请求
|
||||
ajax(`${supervisionAjhcCheckTrueSituationUrl}${encodeURIComponent(curCompletionOuterId)}`, { |
||||
method: 'GET' |
||||
}) |
||||
.then((data: any) => { |
||||
console.log('检查是否需要填表Success:', data); |
||||
sendResponse({ status: 'success', data }); |
||||
}) |
||||
.catch((error: any) => { |
||||
console.error('检查是否需要填表Error:', error); |
||||
sendResponse({ status: 'error', error }); |
||||
}); |
||||
} else { |
||||
console.error('curCompletionOuterId is null, cannot send request'); |
||||
sendResponse({ status: 'error', error: 'curCompletionOuterId is null' }); |
||||
} |
||||
return true; |
||||
} |
||||
}); |
||||
|
||||
/* |
||||
插件更新升级 |
||||
*/ |
||||
|
||||
checkUpdate(); // 插件刚打开时立即运行一次检查更新方法
|
||||
setInterval(checkUpdate, 600000); // 每10分钟检查一次
|
||||
|
||||
// 版本检查
|
||||
async function checkUpdate() { |
||||
try { |
||||
const response = await fetch(checkUpdateUrl); |
||||
const data = await response.json(); |
||||
const currentVersion = chrome.runtime.getManifest().version; |
||||
if (compareVersions(data.version, currentVersion) > 0) { |
||||
showUpdateNotification(data.downloadUrl); |
||||
} |
||||
} catch (error) { |
||||
console.error('检测更新失败:', error); |
||||
} |
||||
} |
||||
|
||||
// 版本号比较
|
||||
function compareVersions(a: string, b: string): number { |
||||
const aParts = a.split('.').map(Number); |
||||
const bParts = b.split('.').map(Number); |
||||
|
||||
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { |
||||
const aVal = aParts[i] || 0; |
||||
const bVal = bParts[i] || 0; |
||||
if (aVal !== bVal) return aVal - bVal; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
// 显示更新通知
|
||||
function showUpdateNotification(downloadUrl: string) { |
||||
console.log('发现新版本,显示更新通知'); |
||||
chrome.notifications.create({ |
||||
type: 'basic', |
||||
iconUrl: 'img/logo-128.png', |
||||
title: '发现新版本,为避免出错,请立即更新!', |
||||
message: '点击此处下载数字督查最新版本插件', |
||||
priority: 2 |
||||
}); |
||||
|
||||
// 保存下载URL
|
||||
chrome.storage.local.set({ updateUrl: downloadUrl }); |
||||
} |
||||
|
||||
// 处理通知点击
|
||||
chrome.notifications.onClicked.addListener(() => { |
||||
chrome.storage.local.get('updateUrl', (result) => { |
||||
if (result.updateUrl) { |
||||
chrome.tabs.create({ url: result.updateUrl }); |
||||
} |
||||
}); |
||||
}); |
||||
@ -0,0 +1,113 @@
|
||||
import {ajhcInfoUrl, ajhcSysUrl} from "../../../globalConfig"; |
||||
|
||||
// 获取样本源头编号和问题发现时间
|
||||
function getSampleData(): { sampleSourceNumber: string | null, issueDiscoveryTime: string | null } { |
||||
const elements = Array.from(document.querySelectorAll('div.col.col-6')); |
||||
let sampleSourceNumber: string | null = null; |
||||
let issueDiscoveryTime: string | null = null; |
||||
|
||||
for (const element of elements) { |
||||
const labelElement = element.querySelector('label'); |
||||
const spanElement = element.querySelector('span'); |
||||
|
||||
if (labelElement && spanElement) { |
||||
if (labelElement.textContent === '样本源头编号') { |
||||
sampleSourceNumber = spanElement.textContent; |
||||
} else if (labelElement.textContent === '问题发现时间') { |
||||
issueDiscoveryTime = spanElement.textContent; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return { sampleSourceNumber, issueDiscoveryTime }; |
||||
} |
||||
|
||||
// 发送样本源头编号和问题发现时间到指定接口
|
||||
function sendData(sampleSourceNumber: string, issueDiscoveryTime: string) { |
||||
fetch(ajhcSysUrl + '/data/caseVerif', { |
||||
method: 'POST', |
||||
headers: { |
||||
'Content-Type': 'application/json', |
||||
}, |
||||
body: JSON.stringify({ sampleSourceNumber, issueDiscoveryTime }), |
||||
}) |
||||
.then(response => response.json()) |
||||
.then(data => console.log('Success:', data)) |
||||
.catch((error) => console.error('Error:', error)); |
||||
} |
||||
|
||||
// 持续监控页面
|
||||
function monitorPage() { |
||||
let lastSampleSourceNumber: string | null = null; |
||||
let lastIssueDiscoveryTime: string | null = null; |
||||
|
||||
setInterval(() => { |
||||
const { sampleSourceNumber, issueDiscoveryTime } = getSampleData(); |
||||
if (sampleSourceNumber && issueDiscoveryTime && |
||||
(sampleSourceNumber !== lastSampleSourceNumber || issueDiscoveryTime !== lastIssueDiscoveryTime)) { |
||||
console.log('已找到样本源头编号和问题发现时间:', sampleSourceNumber, issueDiscoveryTime); |
||||
sendData(sampleSourceNumber, issueDiscoveryTime); |
||||
lastSampleSourceNumber = sampleSourceNumber; // 更新上次检测到的编号
|
||||
lastIssueDiscoveryTime = issueDiscoveryTime; // 更新上次检测到的时间
|
||||
} |
||||
}, 1000); // 每秒检查一次
|
||||
} |
||||
|
||||
let isMonitoring = false; // 添加标志位
|
||||
|
||||
// 使用 MutationObserver 监控 URL 变化
|
||||
function observeUrlChange() { |
||||
const observer = new MutationObserver(() => { |
||||
if (!isMonitoring && window.location.href.includes(ajhcInfoUrl)) { |
||||
console.log('URL 包含指定路径,开始监控页面'); |
||||
monitorPage(); |
||||
isMonitoring = true; // 设置标志位为 true
|
||||
observer.disconnect(); // 断开观察
|
||||
} |
||||
checkAndShowWarning(); |
||||
}); |
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true }); |
||||
} |
||||
|
||||
// 执行 URL 监控函数
|
||||
observeUrlChange(); |
||||
|
||||
// 创建警告消息的 div 元素
|
||||
function createWarningDiv(): HTMLElement { |
||||
const warningDiv = document.createElement('div'); |
||||
warningDiv.style.position = 'fixed'; |
||||
warningDiv.style.top = '10px'; |
||||
warningDiv.style.right = '100px'; |
||||
warningDiv.style.color = 'red'; |
||||
warningDiv.style.fontWeight = 'bold'; |
||||
warningDiv.textContent = '请登录插件,否则数据无法同步至一体化平台!'; |
||||
warningDiv.classList.add('crx-login-warning'); |
||||
warningDiv.style.display = 'none'; // 默认隐藏
|
||||
document.body.appendChild(warningDiv); |
||||
return warningDiv; |
||||
} |
||||
|
||||
// 显示警告消息的函数
|
||||
function showWarningMessage(warningDiv: HTMLElement, showMessage: boolean) { |
||||
warningDiv.style.display = showMessage ? 'block' : 'none'; |
||||
} |
||||
|
||||
// 检查并显示警告的函数
|
||||
function checkAndShowWarning() { |
||||
const warningDiv = createWarningDiv(); // 创建警告 div
|
||||
setInterval(() => { |
||||
console.log('检查并显示警告'); |
||||
if (window.location.href.includes(ajhcSysUrl)) { |
||||
chrome.storage.local.get(['srcAuthToken'], (result) => { |
||||
const token = result.srcAuthToken; |
||||
if (!token) { |
||||
console.log('未找到 srcAuthToken'); |
||||
showWarningMessage(warningDiv, true); // 显示警告
|
||||
} else { |
||||
showWarningMessage(warningDiv, false); // 隐藏警告
|
||||
} |
||||
}); |
||||
} |
||||
}, 5000); // 每5秒检查一次
|
||||
} |
||||
@ -0,0 +1 @@
|
||||
console.info('contentScript is running') |
||||
@ -0,0 +1,207 @@
|
||||
import {ajhcOutInfoUrl} from "../../../globalConfig"; |
||||
|
||||
/* |
||||
获取投诉受理数据 |
||||
*/ |
||||
// 获取样本源头编号和问题发现时间
|
||||
function getSampleData(): { caseNumber: string | null, caseSource: string | null, complaintTime: string | null, reporterName: string | null, reporterContact: string | null, verifiedObjectUnit: string | null, verifiedObjectUnitCode: string | null, verifiedObjectName: string | null, verifiedObjectPosition: string | null, briefCase: string | null, organization: string | null, complaintIssueNature: string | null, acceptanceLevel: string | null } { |
||||
console.log('开始获取元素'); |
||||
|
||||
// 根据文本内容查找元素
|
||||
function findElementByText(selector: string, text: string): HTMLElement | null { |
||||
const elements = document.querySelectorAll(selector); |
||||
for (const element of Array.from(elements)) { |
||||
if (element.textContent && element.textContent.trim().includes(text)) { |
||||
return element as HTMLElement; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
// 获取基本信息栏中的数据
|
||||
const basicInfoTitle = findElementByText('div.ant-card-head-title', '基本信息'); |
||||
console.log('获取到basicInfoTitle' + basicInfoTitle); |
||||
let caseNumber = null; |
||||
let caseSource = null; |
||||
let complaintTime = null; |
||||
if (basicInfoTitle) { |
||||
const basicInfoSection = basicInfoTitle.closest('.ant-card')?.querySelector('.ant-card-body'); |
||||
console.log('获取到basicInfoSection' + basicInfoSection); |
||||
if (basicInfoSection) { |
||||
const caseNumberLabel = basicInfoSection.querySelector('div.ant-col.ant-form-item-label:has(label[title="案件编号"])'); |
||||
caseNumber = caseNumberLabel?.nextElementSibling?.querySelector('span.ant-form-item-children span')?.textContent || null; |
||||
console.log('获取到caseNumber' + caseNumber); |
||||
const caseSourceLabel = basicInfoSection.querySelector('div.ant-col.ant-form-item-label:has(label[title="案件来源"])'); |
||||
caseSource = caseSourceLabel?.nextElementSibling?.querySelector('span.ant-form-item-children')?.textContent || null; |
||||
console.log('获取到caseSource' + caseSource); |
||||
const complaintTimeLabel = basicInfoSection.querySelector('div.ant-col.ant-form-item-label:has(label[title="投诉时间"])'); |
||||
complaintTime = complaintTimeLabel?.nextElementSibling?.querySelector('span.ant-form-item-children')?.textContent || null; |
||||
console.log('获取到complaintTime' + complaintTime); |
||||
} else { |
||||
console.log('basicInfoSection没有找到'); |
||||
} |
||||
} else { |
||||
console.log('基本信息没有找到'); |
||||
} |
||||
|
||||
// 获取举报人信息栏中的数据
|
||||
const reporterInfoTitle = findElementByText('div.ant-card-head-title', '举报人信息'); |
||||
let reporterName = null; |
||||
let reporterContact = null; |
||||
if (reporterInfoTitle) { |
||||
const reporterInfoSection = reporterInfoTitle.closest('.ant-card')?.querySelector('.ant-card-body'); |
||||
console.log('获取到reporterInfoSection' + reporterInfoSection); |
||||
if (reporterInfoSection) { |
||||
const reporterNameLabel = reporterInfoSection.querySelector('div.ant-col.ant-form-item-label:has(label[title="举报人姓名"])'); |
||||
reporterName = reporterNameLabel?.nextElementSibling?.querySelector('span.ant-form-item-children')?.textContent || null; |
||||
console.log('获取到reporterName' + reporterName); |
||||
const reporterContactLabel = reporterInfoSection.querySelector('div.ant-col.ant-form-item-label:has(label[title="联系方式"])'); |
||||
reporterContact = reporterContactLabel?.nextElementSibling?.querySelector('span.ant-form-item-children')?.textContent || null; |
||||
console.log('获取到reporterContact' + reporterContact); |
||||
} else { |
||||
console.log('reporterInfoSection没有找到'); |
||||
} |
||||
} else { |
||||
console.log('举报人信息没有找到'); |
||||
} |
||||
|
||||
// 获取被核查对象信息栏中的数据
|
||||
const verifiedObjectTitle = findElementByText('div.ant-card-head-title', '被核查对象信息'); |
||||
let verifiedObjectUnit = null; |
||||
let verifiedObjectUnitCode = null; |
||||
let verifiedObjectName = null; |
||||
let verifiedObjectPosition = null; |
||||
if (verifiedObjectTitle) { |
||||
const verifiedObjectSection = verifiedObjectTitle.closest('.ant-card')?.querySelector('.ant-card-body'); |
||||
console.log('获取到verifiedObjectSection' + verifiedObjectSection); |
||||
if (verifiedObjectSection) { |
||||
const verifiedObjectUnitLabel = verifiedObjectSection.querySelector('div.ant-col.ant-form-item-label:has(label[title="单位"])'); |
||||
verifiedObjectUnit = verifiedObjectUnitLabel?.nextElementSibling?.querySelector('span.ant-form-item-children')?.textContent || null; |
||||
console.log('获取到verifiedObjectUnit' + verifiedObjectUnit); |
||||
const verifiedObjectUnitCodeString = verifiedObjectUnitLabel?.nextElementSibling?.querySelector('span.ant-form-item-children a')?.getAttribute('href') || null; |
||||
if (verifiedObjectUnitCodeString !== null) { |
||||
const verifiedObjectUnitCodeMatch = verifiedObjectUnitCodeString.match(/bjbdw_(\d+)/); |
||||
if (verifiedObjectUnitCodeMatch && verifiedObjectUnitCodeMatch[1]) { |
||||
verifiedObjectUnitCode = verifiedObjectUnitCodeMatch[1]; |
||||
} |
||||
} |
||||
console.log('获取到verifiedObjectUnitCode' + verifiedObjectUnitCode); |
||||
const verifiedObjectNameLabel = verifiedObjectSection.querySelector('div.ant-col.ant-form-item-label:has(label[title="姓名"])'); |
||||
verifiedObjectName = verifiedObjectNameLabel?.nextElementSibling?.querySelector('span.ant-form-item-children')?.textContent || null; |
||||
console.log('获取到verifiedObjectName' + verifiedObjectName); |
||||
const verifiedObjectPositionLabel = verifiedObjectSection.querySelector('div.ant-col.ant-form-item-label:has(label[title="职务"])'); |
||||
verifiedObjectPosition = verifiedObjectPositionLabel?.nextElementSibling?.querySelector('span.ant-form-item-children')?.textContent || null; |
||||
console.log('获取到verifiedObjectPosition' + verifiedObjectPosition); |
||||
} else { |
||||
console.log('verifiedObjectSection没有找到'); |
||||
} |
||||
} else { |
||||
console.log('被核查对象信息没有找到'); |
||||
} |
||||
|
||||
// 获取投诉案情栏中的数据
|
||||
const complaintCaseTitle = findElementByText('div.ant-card-head-title', '投诉案情'); |
||||
let organization = null; |
||||
let briefCase = null; |
||||
if (complaintCaseTitle) { |
||||
const complaintCaseSection = complaintCaseTitle.closest('.ant-card')?.querySelector('.ant-card-body'); |
||||
console.log('获取到complaintCaseSection' + complaintCaseSection); |
||||
if (complaintCaseSection) { |
||||
const organizationLabel = complaintCaseSection.querySelector('div.ant-col.ant-col-7.org-label'); |
||||
if (organizationLabel && organizationLabel.textContent && organizationLabel.textContent.trim() === "组织机构:") { |
||||
organization = organizationLabel.nextElementSibling?.textContent || null; |
||||
console.log('获取到organization: ' + organization); |
||||
} else { |
||||
console.log('未找到"组织机构"标签'); |
||||
} |
||||
const briefCaseLabel = complaintCaseSection.querySelector('div.ant-col.ant-col-5 span'); |
||||
if (briefCaseLabel && briefCaseLabel.textContent && briefCaseLabel.textContent.trim() === "简要案情:") { |
||||
briefCase = briefCaseLabel?.parentElement?.nextElementSibling?.textContent || null; |
||||
console.log('获取到briefCase:', briefCase); |
||||
} else { |
||||
console.log('未找到"简要案情"标签'); |
||||
} |
||||
} else { |
||||
console.log('complaintCaseSection没有找到'); |
||||
} |
||||
} else { |
||||
console.log('投诉案情信息没有找到'); |
||||
} |
||||
|
||||
// 获取问题类型栏中的数据
|
||||
const questionTypeTitle = findElementByText('div.ant-card-head-title', '问题类型'); |
||||
let complaintIssueNature = null; |
||||
if (questionTypeTitle) { |
||||
const questionTypeSection = questionTypeTitle.closest('.ant-card')?.querySelector('.ant-card-body'); |
||||
console.log('获取到questionTypeSection' + questionTypeSection); |
||||
if (questionTypeSection) { |
||||
const complaintIssueNatureLabel = questionTypeSection.querySelector('div.ant-col.ant-form-item-label:has(label[title="投诉问题性质"])'); |
||||
complaintIssueNature = complaintIssueNatureLabel?.nextElementSibling?.querySelector('ul.ant-select-selection__rendered')?.textContent || null; |
||||
console.log('获取到complaintIssueNature' + complaintIssueNature); |
||||
} else { |
||||
console.log('questionTypeSection没有找到'); |
||||
} |
||||
} else { |
||||
console.log('投诉案情信息没有找到'); |
||||
} |
||||
|
||||
// 获取受理意见栏中的数据
|
||||
const acceptanceOpinionTitle = findElementByText('div.ant-card-head-title', '受理意见'); |
||||
let acceptanceLevel = null; |
||||
if (acceptanceOpinionTitle) { |
||||
const acceptanceOpinionSection = acceptanceOpinionTitle.closest('.ant-card')?.querySelector('.ant-card-body'); |
||||
console.log('获取到acceptanceOpinionSection' + acceptanceOpinionSection); |
||||
if (acceptanceOpinionSection) { |
||||
const acceptanceLevelLabel = acceptanceOpinionSection.querySelector('div.ant-col.ant-form-item-label:has(label.ant-form-item-required[title="拟办方式"])'); |
||||
acceptanceLevel = acceptanceLevelLabel?.nextElementSibling?.querySelector('label.ant-radio-wrapper.ant-radio-wrapper-checked')?.textContent || "本级办理"; |
||||
console.log('获取到acceptanceLevel' + acceptanceLevel); |
||||
} else { |
||||
console.log('acceptanceOpinionSection没有找到'); |
||||
} |
||||
} else { |
||||
console.log('受理意见没有找到'); |
||||
} |
||||
|
||||
return { caseNumber, caseSource, complaintTime, reporterName, reporterContact, verifiedObjectUnit, verifiedObjectUnitCode, verifiedObjectName, verifiedObjectPosition, briefCase, organization, complaintIssueNature, acceptanceLevel }; |
||||
} |
||||
|
||||
/* |
||||
执行获取投诉受理数据 |
||||
*/ |
||||
// 发送样本源头编号和问题发现时间到指定接口
|
||||
function sendData(caseNumber: string | null, caseSource: string | null, complaintTime: string | null, reporterName: string | null, reporterContact: string | null, verifiedObjectUnit: string | null, verifiedObjectUnitCode: string | null, verifiedObjectName: string | null, verifiedObjectPosition: string | null, briefCase: string | null, organization: string | null, complaintIssueNature: string | null, acceptanceLevel: string | null) { |
||||
// 发送消息到 background 脚本
|
||||
chrome.runtime.sendMessage({ action: 'sendAjhcData', data: { caseNumber, caseSource, complaintTime, reporterName, reporterContact, verifiedObjectUnit, verifiedObjectUnitCode, verifiedObjectName, verifiedObjectPosition, briefCase, organization, complaintIssueNature, acceptanceLevel } }, response => { |
||||
console.log('获取投诉受理数据结果:', response); |
||||
}); |
||||
} |
||||
|
||||
// 定期检查确认按钮并执行代码
|
||||
function checkButtonAndExecute() { |
||||
let confirmButtonClickListener: () => void; |
||||
|
||||
setInterval(() => { |
||||
const confirmButton = Array.from(document.querySelectorAll('button.ant-btn.ant-btn-primary')) |
||||
.find(button => button.querySelector('span')?.textContent === '确 认'); |
||||
if (confirmButton && window.location.href.includes(ajhcOutInfoUrl)) { |
||||
console.log('确认按钮已找到'); |
||||
|
||||
// 移除旧的事件监听器
|
||||
if (confirmButtonClickListener) { |
||||
confirmButton.removeEventListener('click', confirmButtonClickListener); |
||||
} |
||||
|
||||
// 定义新的事件监听器并添加
|
||||
confirmButtonClickListener = () => { |
||||
const { caseNumber, caseSource, complaintTime, reporterName, reporterContact, verifiedObjectUnit, verifiedObjectUnitCode, verifiedObjectName, verifiedObjectPosition, briefCase, organization, complaintIssueNature, acceptanceLevel } = getSampleData(); |
||||
console.log('已找到案件信息:', caseNumber, caseSource, complaintTime, reporterName, reporterContact, verifiedObjectUnit, verifiedObjectUnitCode, verifiedObjectName, verifiedObjectPosition, briefCase, organization, complaintIssueNature, acceptanceLevel); |
||||
sendData(caseNumber, caseSource, complaintTime, reporterName, reporterContact, verifiedObjectUnit, verifiedObjectUnitCode, verifiedObjectName, verifiedObjectPosition, briefCase, organization, complaintIssueNature, acceptanceLevel); |
||||
}; |
||||
|
||||
confirmButton.addEventListener('click', confirmButtonClickListener); |
||||
} |
||||
}, 1000); // 每1秒检查一次
|
||||
} |
||||
|
||||
// 执行定期检查函数
|
||||
checkButtonAndExecute(); |
||||
@ -0,0 +1,177 @@
|
||||
import { |
||||
ajhcInProgressUrl, |
||||
} from "../../../globalConfig"; |
||||
|
||||
/* |
||||
通用方法 |
||||
*/ |
||||
// 根据文本内容查找元素
|
||||
function findElementByText(selector: string, text: string): HTMLElement | null { |
||||
const elements = document.querySelectorAll(selector); |
||||
for (const element of Array.from(elements)) { |
||||
if (element.textContent && element.textContent.trim().includes(text)) { |
||||
return element as HTMLElement; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/* |
||||
检查插件是否登录 |
||||
*/ |
||||
// 创建警告消息的 div 元素
|
||||
function createWarningDiv(): HTMLElement { |
||||
const warningDiv = document.createElement('div'); |
||||
warningDiv.style.position = 'fixed'; |
||||
warningDiv.style.top = '10%'; |
||||
warningDiv.style.left = '50%'; |
||||
warningDiv.style.transform = 'translate(-50%, -50%)'; |
||||
warningDiv.style.fontSize = '30px'; |
||||
warningDiv.style.color = 'red'; |
||||
warningDiv.style.fontWeight = 'bold'; |
||||
warningDiv.textContent = '请登录数字督察插件,否则数据无法同步!'; |
||||
warningDiv.classList.add('crx-login-warning'); |
||||
warningDiv.style.display = 'none'; // 默认隐藏
|
||||
document.body.appendChild(warningDiv); |
||||
return warningDiv; |
||||
} |
||||
|
||||
// 显示警告消息的函数
|
||||
function showWarningMessage(warningDiv: HTMLElement, showMessage: boolean) { |
||||
warningDiv.style.display = showMessage ? 'block' : 'none'; |
||||
} |
||||
|
||||
// 检查并显示警告的函数
|
||||
function checkAndShowWarning() { |
||||
const warningDiv = createWarningDiv(); // 创建警告 div
|
||||
setInterval(() => { |
||||
chrome.storage.local.get(['srcAuthToken'], (result) => { |
||||
const token = result.srcAuthToken; |
||||
if (!token) { |
||||
console.log('插件未登录'); |
||||
showWarningMessage(warningDiv, true); // 显示警告
|
||||
} else { |
||||
showWarningMessage(warningDiv, false); // 隐藏警告
|
||||
} |
||||
}); |
||||
}, 5000); // 每5秒检查一次
|
||||
} |
||||
|
||||
checkAndShowWarning(); // 在脚本加载时立即调用 checkAndShowWarning
|
||||
|
||||
/* |
||||
ID列表上传 |
||||
*/ |
||||
let windowOn: boolean = false; |
||||
function checkAndCallUpdateDataRowKeys() { |
||||
if (window.location.href.includes(ajhcInProgressUrl)) { |
||||
const currentResult = document.querySelector('div.antd-pro-pages-workcenter-ajhc-ajhc-hcbg-index-hc'); |
||||
updateDataRowKeys(); |
||||
if (currentResult !== null && !windowOn) { |
||||
|
||||
} |
||||
windowOn = currentResult !== null; |
||||
} |
||||
} |
||||
|
||||
setInterval(checkAndCallUpdateDataRowKeys, 1000); |
||||
|
||||
const sentData = new Set<string>(); // 用于存储已经发送过的 caseNumber
|
||||
|
||||
function updateDataRowKeys() { |
||||
const scroll = document.querySelector('div.ant-table-scroll'); |
||||
if (!scroll) { |
||||
return; |
||||
} |
||||
const dataRowKeyMaps = Array.from(scroll.querySelectorAll('tr.ant-table-row')) |
||||
.map(tr => { |
||||
const outerId = tr.getAttribute('data-row-key'); |
||||
const caseNumber = tr.querySelector('.ant-table-fixed-columns-in-body.ant-table-row-cell-break-word a')?.textContent; |
||||
return { caseNumber, outerId }; |
||||
}) |
||||
.filter(({ caseNumber }) => caseNumber !== null && caseNumber !== undefined && !sentData.has(caseNumber)); // 过滤掉已经发送过的 caseNumber
|
||||
|
||||
// 检查 dataRowKeyMaps 是否为空
|
||||
if (dataRowKeyMaps.length > 0) { |
||||
chrome.runtime.sendMessage({ action: 'fetchDataRowKeys', data: dataRowKeyMaps }, response => { |
||||
console.log('上传ID列表结果:', response); |
||||
if (response.status === "success") { |
||||
dataRowKeyMaps.forEach(({ caseNumber }) => { |
||||
if (caseNumber !== null && caseNumber !== undefined) { |
||||
sentData.add(caseNumber); // 更新已发送数据的存储
|
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
检查是否需要补充案件办结信息 |
||||
*/ |
||||
function createCompletionInformationWarningDiv(): HTMLElement { |
||||
const completionInformationWarningDiv = document.createElement('div'); |
||||
completionInformationWarningDiv.style.position = 'fixed'; |
||||
completionInformationWarningDiv.style.top = '0%'; |
||||
completionInformationWarningDiv.style.right = '0%'; |
||||
completionInformationWarningDiv.style.fontSize = '60px'; |
||||
completionInformationWarningDiv.style.color = 'rgb(101 161 255)'; |
||||
completionInformationWarningDiv.style.fontWeight = 'bold'; |
||||
completionInformationWarningDiv.textContent = '请点击右上方的数字督察插件按钮填写补充信息'; |
||||
completionInformationWarningDiv.classList.add('crx-completion-information-warning'); |
||||
completionInformationWarningDiv.style.display = 'none'; // 默认隐藏
|
||||
completionInformationWarningDiv.style.zIndex = '1000'; // 设置z-index确保在最上层
|
||||
|
||||
// 创建 img 元素并设置属性
|
||||
const logoImg = document.createElement('img'); |
||||
logoImg.src = chrome.runtime.getURL('img/logo-128.png'); |
||||
logoImg.style.width = '150px'; |
||||
logoImg.style.height = '150px'; |
||||
|
||||
// 将 img 元素添加到 completionInformationWarningDiv 中
|
||||
completionInformationWarningDiv.appendChild(logoImg); |
||||
|
||||
document.body.appendChild(completionInformationWarningDiv); |
||||
return completionInformationWarningDiv; |
||||
} |
||||
|
||||
function showCompletionInformationWarningMessage(completionInformationWarningDiv: HTMLElement, showMessage: boolean) { |
||||
completionInformationWarningDiv.style.display = showMessage ? 'block' : 'none'; |
||||
} |
||||
|
||||
// 定期检查案件办结的确定按钮并执行代码
|
||||
function checkButtonAndExecute() { |
||||
const completionInformationWarningDiv = createCompletionInformationWarningDiv(); |
||||
|
||||
setInterval(() => { |
||||
const ajbj = findElementByText('div.ant-tabs-tab-active.ant-tabs-tab', '案件办结'); |
||||
const confirmButton = Array.from(document.querySelectorAll('button.ant-btn.ant-btn-primary')) |
||||
.find(button => button.querySelector('span')?.textContent === '确 定') as HTMLButtonElement; |
||||
|
||||
const currentState = Boolean(ajbj && confirmButton && window.location.href.includes(ajhcInProgressUrl)); |
||||
if (currentState) { |
||||
console.log('找到按钮:', confirmButton); |
||||
|
||||
chrome.runtime.sendMessage({action: 'checkTrueSituation'}, response => { |
||||
if (response.data.data !== true) { |
||||
confirmButton.disabled = true; |
||||
showCompletionInformationWarningMessage(completionInformationWarningDiv, true); |
||||
} else { |
||||
confirmButton.disabled = false; |
||||
showCompletionInformationWarningMessage(completionInformationWarningDiv, false); |
||||
} |
||||
}); |
||||
} else { |
||||
showCompletionInformationWarningMessage(completionInformationWarningDiv, false); |
||||
confirmButton.disabled = false; |
||||
} |
||||
// 发送消息到 popup
|
||||
chrome.runtime.sendMessage({ action: 'showUpdateWriteOn', value: currentState }); |
||||
}, 2000); // 每2秒检查一次
|
||||
} |
||||
|
||||
// 执行定期检查函数
|
||||
checkButtonAndExecute(); |
||||
|
||||
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
<script setup lang="ts"> |
||||
import { ref } from 'vue' |
||||
|
||||
const link = ref('https://github.com/guocaoyi/create-chrome-ext') |
||||
</script> |
||||
|
||||
<template> |
||||
<main> |
||||
<h3>DevTools Page</h3> |
||||
|
||||
<a :href="link" target="_blank"> generated by create-chrome-ext </a> |
||||
</main> |
||||
</template> |
||||
|
||||
<style> |
||||
:root { |
||||
font-family: |
||||
system-ui, |
||||
-apple-system, |
||||
BlinkMacSystemFont, |
||||
'Segoe UI', |
||||
Roboto, |
||||
Oxygen, |
||||
Ubuntu, |
||||
Cantarell, |
||||
'Open Sans', |
||||
'Helvetica Neue', |
||||
sans-serif; |
||||
|
||||
color-scheme: light dark; |
||||
background-color: #242424; |
||||
} |
||||
|
||||
@media (prefers-color-scheme: light) { |
||||
:root { |
||||
background-color: #fafafa; |
||||
} |
||||
|
||||
a:hover { |
||||
color: #42b983; |
||||
} |
||||
} |
||||
|
||||
body { |
||||
min-width: 20rem; |
||||
} |
||||
|
||||
main { |
||||
text-align: center; |
||||
padding: 1em; |
||||
margin: 0 auto; |
||||
} |
||||
|
||||
h3 { |
||||
color: #42b983; |
||||
text-transform: uppercase; |
||||
font-size: 1.5rem; |
||||
font-weight: 200; |
||||
line-height: 1.2rem; |
||||
margin: 2rem auto; |
||||
} |
||||
|
||||
a { |
||||
font-size: 0.5rem; |
||||
margin: 0.5rem; |
||||
color: #cccccc; |
||||
text-decoration: none; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,8 @@
|
||||
import { createApp } from 'vue' |
||||
import App from './DevTools.vue' |
||||
|
||||
chrome.devtools.panels.create('VueCrx', '', '../../devtools.html', function () { |
||||
console.log('devtools panel create') |
||||
}) |
||||
|
||||
createApp(App).mount('#app') |
||||
@ -0,0 +1,8 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' { |
||||
import type { DefineComponent } from 'vue' |
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any> |
||||
export default component |
||||
} |
||||
@ -0,0 +1,80 @@
|
||||
<script setup lang="ts"> |
||||
import { ref, onMounted } from 'vue' |
||||
|
||||
const countSync = ref(0) |
||||
const link = ref('https://github.com/guocaoyi/create-chrome-ext') |
||||
|
||||
onMounted(() => { |
||||
chrome.storage.sync.get(['count'], (result) => { |
||||
countSync.value = result.count ?? 0 |
||||
}) |
||||
|
||||
chrome.runtime.onMessage.addListener((request) => { |
||||
if (request.type === 'COUNT') { |
||||
countSync.value = request?.count ?? 0 |
||||
} |
||||
}) |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<main> |
||||
<h3>数字督查</h3> |
||||
</main> |
||||
</template> |
||||
|
||||
<style> |
||||
:root { |
||||
font-family: |
||||
system-ui, |
||||
-apple-system, |
||||
BlinkMacSystemFont, |
||||
'Segoe UI', |
||||
Roboto, |
||||
Oxygen, |
||||
Ubuntu, |
||||
Cantarell, |
||||
'Open Sans', |
||||
'Helvetica Neue', |
||||
sans-serif; |
||||
|
||||
color-scheme: light dark; |
||||
background-color: #242424; |
||||
} |
||||
|
||||
@media (prefers-color-scheme: light) { |
||||
:root { |
||||
background-color: #fafafa; |
||||
} |
||||
|
||||
a:hover { |
||||
color: #42b983; |
||||
} |
||||
} |
||||
|
||||
body { |
||||
min-width: 20rem; |
||||
} |
||||
|
||||
main { |
||||
text-align: center; |
||||
padding: 1em; |
||||
margin: 0 auto; |
||||
} |
||||
|
||||
h3 { |
||||
color: #42b983; |
||||
text-transform: uppercase; |
||||
font-size: 1.5rem; |
||||
font-weight: 200; |
||||
line-height: 1.2rem; |
||||
margin: 2rem auto; |
||||
} |
||||
|
||||
a { |
||||
font-size: 0.5rem; |
||||
margin: 0.5rem; |
||||
color: #cccccc; |
||||
text-decoration: none; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,5 @@
|
||||
import { createApp } from 'vue' |
||||
|
||||
import App from './Options.vue' |
||||
|
||||
createApp(App).mount('#app') |
||||
@ -0,0 +1,563 @@
|
||||
<script setup lang="ts"> |
||||
import { ref, onMounted } from 'vue' |
||||
import { ElTable, ElTableColumn, ElButton } from 'element-plus' |
||||
import { supervisionUrl, morePageUrl } from "../../globalConfig"; |
||||
|
||||
/* |
||||
定义-用户管理 |
||||
*/ |
||||
// 定义用户名和密码的 ref |
||||
const username = ref('') |
||||
const password = ref('') |
||||
|
||||
// 定义是否已登录的状态 |
||||
const isLoggedIn = ref(false) |
||||
|
||||
/* |
||||
定义-表格管理 |
||||
*/ |
||||
// 定义记录列表的 ref |
||||
interface RecordType { |
||||
originId: string; |
||||
crxState: string; |
||||
responderName: string; |
||||
sourceInvolveDepartName: string; |
||||
createTime: string; |
||||
} |
||||
|
||||
const formRecords = ref<RecordType[]>([]); |
||||
// 表格是否正在加载 |
||||
const formLoading = ref(true); |
||||
|
||||
/* |
||||
定义-通用 |
||||
*/ |
||||
// 定义 Options 类型 |
||||
type Options = { |
||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE', |
||||
headers?: Record<string, string>, |
||||
body?: any, |
||||
}; |
||||
|
||||
// 全局参数 writeOn |
||||
const writeOn = ref(false); |
||||
|
||||
/* |
||||
方法-通用 |
||||
*/ |
||||
// 通用请求函数 |
||||
const ajax = async (url: string, options: Options = {}) => { |
||||
let token: string | null; |
||||
const result = await chrome.storage.local.get(['srcAuthToken']); |
||||
token = result.srcAuthToken; |
||||
const headers: Record<string, string> = { |
||||
'Content-Type': 'application/json', |
||||
...options.headers, |
||||
'Authorization': `${token}`, // 使用 Authorization 头传递 token |
||||
}; |
||||
return fetch(`${supervisionUrl}${url}`, { |
||||
method: options.method || 'GET', |
||||
body: options.body ? JSON.stringify(options.body) : undefined, |
||||
headers, |
||||
credentials: 'include', // 确保请求中包含 Cookie |
||||
}).then(response => { |
||||
if (!response.ok) { |
||||
throw new Error('请求失败'); |
||||
} |
||||
return response.json(); |
||||
}); |
||||
}; |
||||
|
||||
// 新增: 获取状态类名的方法 |
||||
const getStatusClass = (crxState: string): string => { |
||||
switch (crxState) { |
||||
case '1': |
||||
return 'status-synced'; |
||||
case '2': |
||||
return 'status-pending'; |
||||
case '3': |
||||
return 'status-completed'; |
||||
default: |
||||
return 'status-unknown'; |
||||
} |
||||
}; |
||||
|
||||
/* |
||||
方法-用户管理 |
||||
*/ |
||||
// 处理登录方法 |
||||
const handleLogin = async () => { |
||||
try { |
||||
const response = await ajax('/login', { |
||||
method: 'POST', |
||||
body: { |
||||
account: username.value, |
||||
password: password.value, |
||||
}, |
||||
}); |
||||
|
||||
if (response.code === 200) { |
||||
console.log('登录成功'); |
||||
const token = response.data.token; |
||||
await chrome.storage.local.set({ srcAuthToken: token }); |
||||
document.cookie = `token=${token}; path=/;`; |
||||
isLoggedIn.value = true; // 设置为已登录 |
||||
await fetchRecords(); |
||||
} else { |
||||
throw new Error('登录失败:', response.message); |
||||
} |
||||
} catch (error) { |
||||
console.error('登录错误:', error); |
||||
alert('登录失败,请检查用户名和密码'); |
||||
} |
||||
}; |
||||
|
||||
// 处理退出登录方法 |
||||
const handleLogout = async () => { |
||||
try { |
||||
await ajax('/logout', { |
||||
method: 'POST', |
||||
}) |
||||
await chrome.storage.local.remove('srcAuthToken') |
||||
// 清空记录并返回登录界面 |
||||
formRecords.value = [] |
||||
isLoggedIn.value = false; // 设置为未登录 |
||||
} catch (error) { |
||||
console.error('退出登录错误:', error) |
||||
await chrome.storage.local.remove('srcAuthToken') |
||||
// 清空记录并返回登录界面 |
||||
formRecords.value = [] |
||||
isLoggedIn.value = false; // 设置为未登录 |
||||
} |
||||
} |
||||
|
||||
/* |
||||
方法-表格管理 |
||||
*/ |
||||
// 获取表格数据 |
||||
const fetchRecords = async () => { |
||||
try { |
||||
const response = await ajax('/crx/ajhc'); |
||||
if (response.code === 200) { |
||||
// 修改: 直接从 response.data 中提取数组 |
||||
formRecords.value = response.data || []; |
||||
console.log('获取记录成功:', formRecords.value) |
||||
} else { |
||||
console.error('获取记录失败:', response.message); |
||||
} |
||||
} catch (error) { |
||||
console.error('获取记录错误:', error); |
||||
formRecords.value = []; |
||||
} finally { |
||||
formLoading.value = false; |
||||
} |
||||
}; |
||||
|
||||
/* |
||||
vue生命周期 |
||||
*/ |
||||
onMounted(async () => { |
||||
const result = await chrome.storage.local.get(['srcAuthToken']); |
||||
const token = result.srcAuthToken; |
||||
if (token) { |
||||
isLoggedIn.value = true; // 设置为已登录 |
||||
fetchRecords(); |
||||
} |
||||
}); |
||||
|
||||
/* |
||||
显示补充内容页面 |
||||
*/ |
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { |
||||
if (request.action === 'showUpdateWriteOn') { |
||||
if (request.value) { |
||||
handleWrite(); |
||||
} else { |
||||
handleBack(); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// 新增处理填写方法,动态生成带有token的iframe URL |
||||
const handleWrite = async () => { |
||||
try { |
||||
const result = await chrome.storage.local.get(['srcAuthToken']); |
||||
const token = result.srcAuthToken; |
||||
if (token) { |
||||
chrome.runtime.sendMessage({ action: 'getCurCompletionOuterId' }, (curCompletionOuterId) => { |
||||
if (curCompletionOuterId) { |
||||
const iframeUrl = `http://65.47.6.108/plugin/#/work/verifySubmit?outerId=${curCompletionOuterId}&token=${token}`; |
||||
writeOn.value = true; |
||||
iframeSrc.value = iframeUrl; |
||||
} else { |
||||
alert('获取 curCompletionOuterId 失败'); |
||||
} |
||||
}); |
||||
} else { |
||||
alert('未登录,请先登录'); |
||||
} |
||||
} catch (error) { |
||||
console.error('获取token失败:', error); |
||||
alert('获取token失败,请稍后再试'); |
||||
} |
||||
}; |
||||
|
||||
// 新增一个ref来存储iframe的src |
||||
const iframeSrc = ref(''); |
||||
|
||||
// 新增处理返回方法 |
||||
const handleBack = () => { |
||||
writeOn.value = false; |
||||
}; |
||||
|
||||
/* |
||||
网页跳转 |
||||
*/ |
||||
const openMorePage = () => { |
||||
window.open(`${morePageUrl}`, '_blank'); |
||||
} |
||||
|
||||
// 弹窗大小样式 |
||||
const defaultPopupStyle = { |
||||
width: '350px', |
||||
height: '400px', |
||||
}; |
||||
|
||||
const largePopupStyle = { |
||||
width: '800px', |
||||
height: '800px', |
||||
}; |
||||
|
||||
</script> |
||||
|
||||
<template> |
||||
<div class="records-header">长沙公安数字督察一体化平台</div> |
||||
<main> |
||||
<!-- 登录界面 --> |
||||
<h3 v-if="!isLoggedIn">登录</h3> |
||||
<div v-if="!isLoggedIn" class="login-form"> |
||||
<input type="text" v-model="username" placeholder="用户名" /> |
||||
<input type="password" v-model="password" placeholder="密码" /> |
||||
<button class="loginButton" @click="handleLogin">登录</button> |
||||
</div> |
||||
|
||||
<!-- 表格界面 --> |
||||
<div v-else-if="!writeOn" class="records-page"> |
||||
<el-button class="logout-button" @click="handleLogout">退出登录</el-button> |
||||
<!-- 填写按钮 --> |
||||
<!-- <el-button class="write-button" @click="handleWrite">填写</el-button>--> |
||||
<div v-if="!formLoading && formRecords.length > 0" class="records-list"> |
||||
<div v-for="(record, index) in formRecords" :key="record.originId" class="record-item"> |
||||
<div class="record-row"> |
||||
<!-- 修改: 使用 getStatusClass 方法动态设置类名 --> |
||||
<span :class="getStatusClass(record.crxState)">{{ record.crxState === '1' ? '已同步' : record.crxState === '2' ? '待完善' : record.crxState === '3' ? '已办结' : '未知状态' }}</span> |
||||
<span class="origin-id">信件编号 {{ record.originId }}</span> |
||||
</div> |
||||
<div class="record-row2"> |
||||
<span class="responder-name">({{ record.responderName }})</span> |
||||
<span class="source-involve-depart-name">{{ record.sourceInvolveDepartName }}</span> |
||||
</div> |
||||
<div class="record-row-time"> |
||||
<span class="create-time" style="margin-left: auto;">{{ record.createTime }}</span> |
||||
</div> |
||||
<div v-if="index < formRecords.length - 1" class="separator"></div> |
||||
</div> |
||||
</div> |
||||
<div v-else-if="formLoading" class="loading-text">加载中···</div> |
||||
<div v-else class="no-data-text">无数据</div> |
||||
|
||||
<!-- 查看更多按钮 --> |
||||
<el-button class="more-button" @click="openMorePage">查看更多记录</el-button> |
||||
</div> |
||||
|
||||
<!-- 填写界面 --> |
||||
<div v-else class="write-page" :style="writeOn ? largePopupStyle : defaultPopupStyle"> |
||||
<!-- <el-button class="back-button" @click="handleBack">返回</el-button>--> |
||||
<iframe :src="iframeSrc" style="width: 100%; height: 800px;"></iframe> |
||||
</div> |
||||
</main> |
||||
</template> |
||||
|
||||
<style> |
||||
:root { |
||||
font-family: |
||||
system-ui, |
||||
-apple-system, |
||||
BlinkMacSystemFont, |
||||
'Segoe UI', |
||||
Roboto, |
||||
Oxygen, |
||||
Ubuntu, |
||||
Cantarell, |
||||
'Open Sans', |
||||
'Helvetica Neue', |
||||
sans-serif; |
||||
|
||||
color-scheme: light dark; |
||||
background-color: #242424; |
||||
} |
||||
|
||||
@media (prefers-color-scheme: light) { |
||||
:root { |
||||
background-color: #fafafa; |
||||
} |
||||
|
||||
a:hover { |
||||
color: #42b983; |
||||
} |
||||
} |
||||
|
||||
body { |
||||
min-width: 300px; |
||||
color-scheme: light dark; |
||||
margin: 0; |
||||
} |
||||
|
||||
main { |
||||
text-align: center; |
||||
margin: 0 auto; |
||||
width: 100%; |
||||
} |
||||
|
||||
h3 { |
||||
color: #19257D; |
||||
text-transform: uppercase; |
||||
font-size: 1.5rem; |
||||
font-weight: 200; |
||||
line-height: 1rem; |
||||
} |
||||
|
||||
a { |
||||
font-size: 0.5rem; |
||||
margin: 0.5rem; |
||||
color: #cccccc; |
||||
text-decoration: none; |
||||
} |
||||
|
||||
/* |
||||
样式-登录 |
||||
*/ |
||||
.login-form { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
margin-top: 1rem; |
||||
} |
||||
|
||||
.login-form input { |
||||
margin: 0.5rem 0; |
||||
padding: 0.5rem; |
||||
font-size: 1rem; |
||||
width: 80%; |
||||
} |
||||
|
||||
.login-form button { |
||||
font-size: 1rem; |
||||
padding: 0.5rem 1rem; |
||||
border: 1px solid #0914b1; |
||||
border-radius: 0.25rem; |
||||
background-color: #0914b1; |
||||
color: white; |
||||
cursor: pointer; |
||||
outline: none; |
||||
} |
||||
|
||||
/* |
||||
样式-表格 |
||||
*/ |
||||
.records-page { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
width: 350px; |
||||
height: 400px; |
||||
position: relative; |
||||
} |
||||
|
||||
./**/logout-button { |
||||
align-self: flex-end; |
||||
margin: 0.5rem; |
||||
font-size: 13px; |
||||
padding: 0.2rem 0.6rem; |
||||
border: 1px solid white; |
||||
border-radius: 0.25rem; |
||||
background-color: white; |
||||
color: #0914b1; |
||||
cursor: pointer; |
||||
outline: none; |
||||
} |
||||
|
||||
.loginButton { |
||||
margin: 0.5rem auto 1rem auto; |
||||
} |
||||
|
||||
.records-header { |
||||
font-size: 1.3rem; |
||||
background-color: #0914b1; |
||||
color: white; |
||||
width: 100%; |
||||
height: 50px; |
||||
text-align: center; /* 文字水平居中 */ |
||||
display: flex; /* 使用 Flexbox 布局 */ |
||||
align-items: center; /* 垂直居中文本 */ |
||||
justify-content: center; /* 水平居中文本,可选,因为已经使用了 text-align: center */ |
||||
} |
||||
|
||||
.status-synced { |
||||
color: white; |
||||
background-color: #19257D; |
||||
border-radius: 3px; |
||||
float: left; |
||||
padding: 3px 10px; |
||||
font-size: 12px; |
||||
} |
||||
|
||||
.status-pending { |
||||
color: white; |
||||
background-color: #F04E00; |
||||
border-radius: 3px; |
||||
float: left; |
||||
padding: 3px 10px; |
||||
font-size: 12px; |
||||
} |
||||
|
||||
.status-completed { |
||||
color: white; |
||||
background-color: #228B22; |
||||
border-radius: 3px; |
||||
float: left; |
||||
padding: 3px 10px; |
||||
font-size: 12px; |
||||
} |
||||
|
||||
.status-unknown { |
||||
color: white; |
||||
background-color: #808080; |
||||
border-radius: 3px; |
||||
float: left; |
||||
padding: 3px 10px; |
||||
font-size: 12px; |
||||
} |
||||
|
||||
.separator{ |
||||
padding: 2px 2px; |
||||
border-top: 1px solid #d3d3d3; /* 添加一根灰色横线 */ |
||||
} |
||||
|
||||
.origin-id{ |
||||
margin: 0 10px; |
||||
} |
||||
|
||||
.record-row{ |
||||
display: flex; |
||||
justify-content: flex-start; |
||||
align-items: center; |
||||
font-size: 13px; |
||||
color: #666666 |
||||
} |
||||
|
||||
.record-row2{ |
||||
display: flex; |
||||
font-size: 15px; |
||||
color: #333333; |
||||
margin: 5px 0; |
||||
} |
||||
|
||||
.record-row-time{ |
||||
display: flex; |
||||
justify-content: flex-end; |
||||
font-size: 12px; |
||||
color: #666666 |
||||
} |
||||
|
||||
.records-list { |
||||
max-height: 350px; |
||||
max-width: 400px; |
||||
overflow-x: auto; |
||||
overflow-y: auto; |
||||
width: 100%; |
||||
padding: 1rem; |
||||
box-sizing: border-box; |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; /* 当内容溢出时显示省略号 */ |
||||
border: 1px solid #ebeef5; |
||||
border-radius: 4px; |
||||
margin-bottom: 35px; |
||||
} |
||||
|
||||
/* 表头样式 */ |
||||
.records-list th { |
||||
background-color: #f5f7fa; |
||||
color: #909399; |
||||
font-weight: bold; |
||||
padding: 12px; |
||||
text-align: left; |
||||
} |
||||
|
||||
/* 单元格样式 */ |
||||
.records-list td { |
||||
padding: 12px; |
||||
border-bottom: 1px solid #ebeef5; |
||||
} |
||||
|
||||
/* 悬停效果 */ |
||||
.records-list tr:hover { |
||||
background-color: #f5f7fa; |
||||
white-space: normal; /* 允许换行 */ |
||||
overflow: visible; /* 显示溢出内容 */ |
||||
text-overflow: clip; /* 去掉省略号 */ |
||||
} |
||||
|
||||
/* 奇偶行背景色 */ |
||||
.records-list tr:nth-child(even) { |
||||
background-color: #f9fafc; |
||||
} |
||||
|
||||
.more-button { |
||||
position: absolute; |
||||
bottom: 0; |
||||
left: 0; |
||||
width: 100%; |
||||
margin-top: 0; |
||||
font-size: 1rem; |
||||
padding: 0.5rem 0; |
||||
border: 1px solid #ECEEFA; |
||||
border-radius: 0.25rem; |
||||
background-color: #ECEEFA; |
||||
color: #19257D; |
||||
cursor: pointer; |
||||
outline: none; |
||||
} |
||||
|
||||
/* |
||||
样式-填写按钮 |
||||
*/ |
||||
.write-button { |
||||
align-self: flex-end; |
||||
margin: 1rem; |
||||
font-size: 1rem; |
||||
padding: 0.5rem 1rem; |
||||
border: 1px solid #0914b1; |
||||
border-radius: 0.25rem; |
||||
background-color: #0914b1; |
||||
color: white; |
||||
cursor: pointer; |
||||
outline: none; |
||||
} |
||||
|
||||
/* |
||||
样式-返回按钮 |
||||
*/ |
||||
.back-button { |
||||
align-self: flex-end; |
||||
margin: 1rem; |
||||
font-size: 1rem; |
||||
padding: 0.5rem 1rem; |
||||
border: 1px solid #0914b1; |
||||
border-radius: 0.25rem; |
||||
background-color: #0914b1; |
||||
color: white; |
||||
cursor: pointer; |
||||
outline: none; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,5 @@
|
||||
import { createApp } from 'vue' |
||||
|
||||
import Popup from './Popup.vue' |
||||
|
||||
createApp(Popup).mount('#app') |
||||
@ -0,0 +1,66 @@
|
||||
<script setup lang="ts"> |
||||
|
||||
|
||||
</script> |
||||
|
||||
<template> |
||||
<main> |
||||
<h3>数字督查</h3> |
||||
</main> |
||||
</template> |
||||
|
||||
<style> |
||||
:root { |
||||
font-family: |
||||
system-ui, |
||||
-apple-system, |
||||
BlinkMacSystemFont, |
||||
'Segoe UI', |
||||
Roboto, |
||||
Oxygen, |
||||
Ubuntu, |
||||
Cantarell, |
||||
'Open Sans', |
||||
'Helvetica Neue', |
||||
sans-serif; |
||||
|
||||
color-scheme: light dark; |
||||
background-color: #242424; |
||||
} |
||||
|
||||
@media (prefers-color-scheme: light) { |
||||
:root { |
||||
background-color: #fafafa; |
||||
} |
||||
|
||||
a:hover { |
||||
color: #42b983; |
||||
} |
||||
} |
||||
|
||||
body { |
||||
min-width: 20rem; |
||||
} |
||||
|
||||
main { |
||||
text-align: center; |
||||
padding: 1em; |
||||
margin: 0 auto; |
||||
} |
||||
|
||||
h3 { |
||||
color: #42b983; |
||||
text-transform: uppercase; |
||||
font-size: 1.5rem; |
||||
font-weight: 200; |
||||
line-height: 1.2rem; |
||||
margin: 2rem auto; |
||||
} |
||||
|
||||
a { |
||||
font-size: 0.5rem; |
||||
margin: 0.5rem; |
||||
color: #cccccc; |
||||
text-decoration: none; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,5 @@
|
||||
import { createApp } from 'vue' |
||||
|
||||
import SidePanel from './SidePanel.vue' |
||||
|
||||
createApp(SidePanel).mount('#app') |
||||
@ -0,0 +1,20 @@
|
||||
export function ajax(url: string, options: { method: string, body?: any }): Promise<any> { |
||||
let token: string | null; |
||||
return chrome.storage.local.get(['srcAuthToken']).then(result => { |
||||
token = result.srcAuthToken; |
||||
return fetch(url, { |
||||
method: options.method, |
||||
headers: { |
||||
'Content-Type': 'application/json', |
||||
'Authorization': `${token}`, // 使用 Authorization 头传递 token
|
||||
}, |
||||
body: JSON.stringify(options.body), |
||||
credentials: 'include', // 确保请求中包含 Cookie
|
||||
}).then(response => { |
||||
if (!response.ok) { |
||||
throw new Error('请求失败'); |
||||
} |
||||
return response.json(); |
||||
}); |
||||
}); |
||||
} |
||||
@ -0,0 +1,10 @@
|
||||
import gulp from 'gulp' |
||||
import zip from 'gulp-zip' |
||||
import { createRequire } from 'module' |
||||
const require = createRequire(import.meta.url) |
||||
const manifest = require('../build/manifest.json') |
||||
|
||||
gulp |
||||
.src('build/**') |
||||
.pipe(zip(`${manifest.name.replaceAll(' ', '-')}-${manifest.version}.zip`)) |
||||
.pipe(gulp.dest('package')) |
||||
@ -0,0 +1,18 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"target": "ESNext", |
||||
"useDefineForClassFields": true, |
||||
"module": "ESNext", |
||||
"moduleResolution": "Node", |
||||
"strict": true, |
||||
"jsx": "preserve", |
||||
"sourceMap": true, |
||||
"resolveJsonModule": true, |
||||
"isolatedModules": true, |
||||
"esModuleInterop": true, |
||||
"lib": ["ESNext", "DOM"], |
||||
"skipLibCheck": true |
||||
}, |
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], |
||||
"references": [{ "path": "./tsconfig.node.json" }] |
||||
} |
||||
@ -0,0 +1,8 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"composite": true, |
||||
"module": "ESNext", |
||||
"moduleResolution": "Node" |
||||
}, |
||||
"include": ["vite.config.ts"] |
||||
} |
||||
@ -0,0 +1,4 @@
|
||||
{ |
||||
"version": "1.0.1", |
||||
"downloadUrl": "http://65.47.6.108/extension/download.html" |
||||
} |
||||
@ -0,0 +1,23 @@
|
||||
import { defineConfig } from 'vite' |
||||
import { crx } from '@crxjs/vite-plugin' |
||||
import vue from '@vitejs/plugin-vue' |
||||
import manifest from './src/manifest' |
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }) => { |
||||
const production = mode === 'production' |
||||
|
||||
return { |
||||
build: { |
||||
cssCodeSplit: true, |
||||
emptyOutDir: true, |
||||
outDir: 'build', |
||||
rollupOptions: { |
||||
output: { |
||||
chunkFileNames: 'assets/chunk-[hash].js', |
||||
}, |
||||
}, |
||||
}, |
||||
plugins: [crx({ manifest }), vue()], |
||||
} |
||||
}) |
||||