@ -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()], |
||||||
|
} |
||||||
|
}) |
||||||