├── .babelrc.js
├── .browserslistrc
├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── .husky
├── commit-msg
├── pre-commit
└── pre-push-todo-open
├── .prettierignore
├── .prettierrc
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── global.d.ts
├── package.json
├── src
├── api
│ ├── axios.ts
│ ├── files
│ │ └── admin.ts
│ ├── putApi.ts
│ └── useAxios.ts
├── components.d.ts
├── config
│ └── index.ts
├── hooks
│ └── index.ts
├── manifest.json
├── pages
│ ├── background
│ │ └── index.ts
│ ├── content
│ │ ├── App.vue
│ │ ├── images
│ │ │ ├── bg.png
│ │ │ ├── load_icon.png
│ │ │ └── settings.png
│ │ ├── index.ts
│ │ └── store
│ │ │ ├── index.ts
│ │ │ └── modules
│ │ │ └── user.ts
│ ├── option
│ │ ├── App.vue
│ │ ├── images
│ │ │ ├── bg.png
│ │ │ ├── logolan.png
│ │ │ └── settings.png
│ │ ├── index.ts
│ │ ├── option.html
│ │ └── store
│ │ │ ├── index.ts
│ │ │ └── modules
│ │ │ └── user.ts
│ └── popup
│ │ ├── App.vue
│ │ ├── images
│ │ ├── bg.png
│ │ ├── logolan.png
│ │ └── settings.png
│ │ ├── index.ts
│ │ ├── popup.html
│ │ └── store
│ │ ├── index.ts
│ │ └── modules
│ │ └── user.ts
├── style
│ └── main.css
├── types
│ ├── auto-imports.d.ts
│ └── globle.d.ts
└── utils
│ └── index.ts
├── static
└── img
│ ├── bg.png
│ ├── icon.png
│ └── settings.png
├── tsconfig.json
├── types
├── auto-imports.d.ts
├── components.d.ts
└── custom-types.d.ts
└── webpack.config.ts
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }],
4 | ['@babel/typescript', { allExtensions: true }],
5 | ],
6 | }
7 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | edge >= 79
2 | firefox >= 78
3 | chrome >= 64
4 | safari >= 12
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 | quote_type = single
11 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["@element-plus/eslint-config"]
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DS_Store
4 | pnpm-lock.yaml
5 | chrome
6 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx --no-install commitlint --edit $1
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | pnpm exec lint-staged
5 | pnpm exec pretty-quick --staged
6 |
--------------------------------------------------------------------------------
/.husky/pre-push-todo-open:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npm run test
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "overrides": [
5 | {
6 | "files": ".prettierrc",
7 | "options": {
8 | "parser": "json"
9 | }
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "callbackindex",
4 | "chromcallback"
5 | ]
6 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Element Plus
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
vue-chrome-extension
2 |
3 | ## 简介
4 | chrome扩展开发插件基于vue3、ts、Element Plus、Webpack5、axios、less开发
5 | 支持content快速调用chrome对象及axios 详看 pages/content/app.vue
6 | 开箱即用chrome插件
7 |
8 | ## 特性
9 | - **基础框架**:使用 Vue3/Element Plus
10 | - **TypeScript**: 应用程序级 JavaScript 的语言
11 |
12 | ## 安装使用
13 |
14 | - 获取项目代码
15 |
16 | ```bash
17 | git clone https://github.com/choumai555/vue-chrome-extension-mv3.git
18 | ```
19 |
20 | - 安装依赖
21 |
22 | ```bash
23 |
24 | cnpm install
25 |
26 | ```
27 |
28 | - 运行
29 |
30 | ```bash
31 | npm run watch
32 | ```
33 |
--------------------------------------------------------------------------------
/global.d.ts:
--------------------------------------------------------------------------------
1 | // GlobalComponents for Volar
2 | declare module 'vue' {
3 | export interface GlobalComponents {
4 |
5 | }
6 | }
7 |
8 | export { }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chrome-extension",
3 | "license": "MIT",
4 | "private": true,
5 | "scripts": {
6 | "watch": "webpack --progress --mode development --watch",
7 | "commit": "git add . && git-cz",
8 | "commit:ignore": "git add . && git-cz -n"
9 | },
10 | "dependencies": {
11 | "core-js": "^3.21.1",
12 | "element-plus": "^2.1.0",
13 | "git-cz": "^4.9.0",
14 | "unplugin-auto-import": "^0.15.2",
15 | "unplugin-vue-define-options": "^1.3.3",
16 | "vue": "^3.2.31"
17 | },
18 | "devDependencies": {
19 | "prettier": "^2.6.2",
20 | "husky": "^7.0.4",
21 | "lint-staged": "^12.3.7",
22 | "@babel/core": "^7.17.5",
23 | "@babel/preset-env": "^7.16.11",
24 | "@babel/preset-typescript": "^7.16.7",
25 | "@types/jquery": "^3.5.14",
26 | "@types/node": "^17.0.25",
27 | "@types/webpack": "^5.28.0",
28 | "@vespaiach/axios-fetch-adapter": "^0.2.2",
29 | "axios": "^0.26.1",
30 | "babel-loader": "^8.2.3",
31 | "copy-webpack-plugin": "^5.0.3",
32 | "cross-env": "^7.0.3",
33 | "css-loader": "^6.7.1",
34 | "html-webpack-plugin": "^5.5.0",
35 | "jquery": "^3.6.0",
36 | "less": "^3.11.1",
37 | "less-loader": "^10.2.0",
38 | "pinia": "^2.0.13",
39 | "style-loader": "^3.3.1",
40 | "sucrase": "^3.20.3",
41 | "typescript": "^4.6.2",
42 | "unplugin-vue-components": "^0.18.0",
43 | "url-loader": "^4.1.1",
44 | "vue-loader": "^17.0.0",
45 | "webpack": "^5.70.0",
46 | "webpack-cli": "^4.9.2",
47 | "webpack-dev-server": "^4.7.4"
48 | },
49 | "lint-staged": {
50 | "*.{vue,js,ts,jsx,tsx,md,json}": "eslint --fix"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/api/axios.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/mzabriskie/axios
2 | import axios, { AxiosResponse } from 'axios'
3 | import fetchAdapter from '@vespaiach/axios-fetch-adapter';
4 | const http = axios.create({
5 | adapter: fetchAdapter
6 | })
7 | // 允许携带cookie
8 | http.defaults.withCredentials = true
9 | // 拦截request,设置全局请求为ajax请求
10 | axios.interceptors.request.use((config) => {
11 | //@ts-ignore
12 | config.headers['X-Requested-With'] = 'XMLHttpRequest'
13 | return config
14 | })
15 | // 拦截响应response,并做一些错误处理
16 | axios.interceptors.response.use((response) => {
17 | const data = response.data
18 | let err = {
19 | data: null,
20 | response: null as unknown as AxiosResponse
21 | };
22 | err.data = data
23 | err.response = response
24 | throw err
25 | }, (err) => {
26 | // 这里是返回状态码不为200时候的错误处理
27 | if (err && err.response) {
28 | switch (err.response.status) {
29 | case 400:
30 | err.message = '请求错误'
31 | break
32 |
33 | case 401:
34 | err.message = '未授权,请登录'
35 | break
36 |
37 | case 403:
38 | err.message = '拒绝访问'
39 | break
40 |
41 | case 404:
42 | err.message = `请求地址出错: ${err.response.config.url}`
43 | break
44 |
45 | case 408:
46 | err.message = '请求超时'
47 | break
48 |
49 | case 500:
50 | err.message = `服务器内部错误: ${err.response.data.error.message}`
51 | break
52 |
53 | case 501:
54 | err.message = '服务未实现'
55 | break
56 |
57 | case 502:
58 | err.message = '网关错误'
59 | break
60 |
61 | case 503:
62 | err.message = '服务不可用'
63 | break
64 |
65 | case 504:
66 | err.message = '网关超时'
67 | break
68 |
69 | case 505:
70 | err.message = 'HTTP版本不受支持'
71 | break
72 |
73 | default:
74 | }
75 | }
76 | return Promise.reject(err)
77 | })
78 | export default http
--------------------------------------------------------------------------------
/src/api/files/admin.ts:
--------------------------------------------------------------------------------
1 | import putApi from '../putApi'
2 | var api = {};
3 | putApi(api, "test", "https://fanyi.baidu.com/pc/config", {
4 | method: 'get'
5 | });
6 |
7 | export default api;
--------------------------------------------------------------------------------
/src/api/putApi.ts:
--------------------------------------------------------------------------------
1 | import axios from './axios'
2 | export default function (api: { [x: string]: (body: any, callback: (arg0: any) => any, headers?: {}) => void }, name: string | number, url: string, {
3 | method = 'get',
4 | } = {}) {
5 | api[name] = (body: any, callback: (arg0: any) => any, headers = {}) => {
6 | Object.assign(axios.defaults.headers.common, headers)
7 | if (method == 'get') {
8 | axios[method](url, { params: body }).then(callback).catch((res) => {
9 | throw new Error(res)
10 | })
11 | } else {
12 | //@ts-ignore
13 | axios[method](url, body).then(callback).catch((res: any) => {
14 | throw new Error(res)
15 | })
16 | }
17 | }
18 | };
--------------------------------------------------------------------------------
/src/api/useAxios.ts:
--------------------------------------------------------------------------------
1 | var api = {};
2 | //@ts-ignore
3 | import admin from './files/admin'
4 | Object.assign(api, {
5 | ...admin
6 | })
7 | export function axios(request: { funName: any; pramas: any; headers: any; }, sender: any, callback = () => { }) {
8 | let { funName, pramas, headers } = request;
9 | //@ts-ignore
10 | if (funName in api) api[funName](pramas, (...pramas) => callback(pramas), headers)
11 | else throw new Error('未找到对应的chrome方法')
12 | }
13 | export const serve = api
--------------------------------------------------------------------------------
/src/components.d.ts:
--------------------------------------------------------------------------------
1 | // generated by unplugin-vue-components
2 | // We suggest you to commit this file into source control
3 | // Read more: https://github.com/vuejs/vue-next/pull/3399
4 |
5 | declare module 'vue' {
6 | export interface GlobalComponents {
7 | ElInput: typeof import('element-plus/es')['ElInput']
8 | }
9 | }
10 |
11 | export { }
12 |
--------------------------------------------------------------------------------
/src/config/index.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | api: '*',
3 | };
--------------------------------------------------------------------------------
/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 |
2 | export function useChrome(funName: string, ...pramas: any) {
3 | let callback = function () { }
4 | let callbackindex = pramas.findIndex((item: any) => typeof item === 'function')
5 | if (callbackindex != -1) {
6 | callback = pramas[callbackindex]
7 | pramas[callbackindex] = { callback: true }
8 | }
9 | chrome.runtime.sendMessage({
10 | funType: 'chrome',
11 | funName: funName,
12 | pramas,
13 | //@ts-ignore
14 | }, (pramas) => callback(...pramas));
15 | }
16 | export function axios(funName: string, pramas: any, callback: (arg0: any) => any, headers = {}) {
17 | chrome.runtime.sendMessage({
18 | funType: 'axios',
19 | funName,
20 | pramas,
21 | headers
22 | //@ts-ignore
23 | }, (pramas) => callback(...pramas));
24 | }
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "助手",
4 | "version": "1.0.0",
5 | "description": "助手",
6 | "content_security_policy": {
7 | "script-src": "self",
8 | "object-src": "self"
9 | },
10 | "icons": {
11 | "16": "static/img/icon.png",
12 | "19": "static/img/icon.png",
13 | "38": "static/img/icon.png",
14 | "48": "static/img/icon.png",
15 | "128": "static/img/icon.png"
16 | },
17 | "action": {
18 | "default_icon": "static/img/icon.png",
19 | "default_title": "助手",
20 | "default_popup": "popup.html"
21 | },
22 | "options_page": "option.html",
23 | "background": {
24 | "service_worker": "background.main.js"
25 | },
26 | "content_scripts": [{
27 | "matches": [
28 | "*://*/*"
29 | ],
30 | "css": [],
31 | "js": ["content.main.js"],
32 | "run_at": "document_end"
33 | }],
34 | "host_permissions": [
35 | "*://*/*"
36 | ],
37 | "permissions": [
38 | "declarativeNetRequest",
39 | "declarativeNetRequestFeedback",
40 | "contextMenus",
41 | "webRequest",
42 | "tabs",
43 | "activeTab",
44 | "notifications",
45 | "storage",
46 | "unlimitedStorage",
47 | "downloads",
48 | "cookies",
49 | "management",
50 | "webNavigation"
51 | ]
52 | }
--------------------------------------------------------------------------------
/src/pages/background/index.ts:
--------------------------------------------------------------------------------
1 | import { axios } from '@/api/useAxios'
2 | //调用chrome事件
3 | function chromeEvent(request: { funName: any; pramas: any; }, sender: any, callback: (arg0: any[]) => any) {
4 | let { funName, pramas } = request;
5 | let funCode = funName.split('.');
6 | let chromeFun = chrome;
7 | const chromeCallback = (...arr: any[]) => callback(arr)
8 | funCode.forEach((item: string, index: number) => {
9 | if (typeof chromeFun[item] !== 'undefined')
10 | //最后一个参数
11 | if (index + 1 === funCode.length) {
12 | if (typeof chromeFun[item] === 'function') {
13 | let callbackindex = pramas.findIndex((item: any) => typeof item === 'object' && 'callback' in item && item['callback'])
14 | if (callbackindex != -1) pramas[callbackindex] = chromeCallback;
15 | chromeFun[item](...pramas)
16 | } else throw new Error('未找到对应的chrome方法')
17 | } else {
18 | chromeFun = chromeFun[item]
19 | }
20 | })
21 | }
22 | chrome.runtime.onMessage.addListener(function (request: any, sender: any, sendResponse: any) {
23 | switch (request.funType) {
24 | case 'chrome': //代理执行chrome方法
25 | chromeEvent(request, sender, sendResponse)
26 | break;
27 | case 'axios': //代理执行chrome方法
28 | axios(request, sender, sendResponse)
29 | break;
30 | }
31 | //处理异步响应
32 | return true
33 | });
--------------------------------------------------------------------------------
/src/pages/content/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
28 |
--------------------------------------------------------------------------------
/src/pages/content/images/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/src/pages/content/images/bg.png
--------------------------------------------------------------------------------
/src/pages/content/images/load_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/src/pages/content/images/load_icon.png
--------------------------------------------------------------------------------
/src/pages/content/images/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/src/pages/content/images/settings.png
--------------------------------------------------------------------------------
/src/pages/content/index.ts:
--------------------------------------------------------------------------------
1 | $(``).appendTo(document.body);
2 | import { createApp } from 'vue'
3 | import App from './App.vue'
4 | import ElementPlus from 'element-plus'
5 | import 'element-plus/dist/index.css'
6 | let app
7 | function render() {
8 | app = createApp(App)
9 | app.use(ElementPlus)
10 | app.mount('#content_app')
11 | }
12 | render()
--------------------------------------------------------------------------------
/src/pages/content/store/index.ts:
--------------------------------------------------------------------------------
1 | import type { App } from 'vue';
2 | import { createPinia } from 'pinia';
3 |
4 | const store = createPinia();
5 |
6 | export function setupStore(app: App
) {
7 | app.use(store);
8 | }
9 |
10 | export { store };
11 |
--------------------------------------------------------------------------------
/src/pages/content/store/modules/user.ts:
--------------------------------------------------------------------------------
1 |
2 | import { defineStore } from 'pinia';
3 | import { store } from '../index';
4 | export const useUserStore = defineStore({
5 | id: 'app-user',
6 | state: () => ({
7 | // user info
8 | userInfo: null,
9 | // token
10 | token: undefined,
11 | // roleList
12 | roleList: [],
13 | // Whether the login expired
14 | sessionTimeout: false,
15 | // Last fetch time
16 | lastUpdateTime: 0,
17 | }),
18 | getters: {
19 | getUserInfo() {
20 | return this.userInfo
21 | },
22 | getToken(): string {
23 | return this.token
24 | },
25 | },
26 | actions: {
27 | setSessionTimeout(flag: boolean) {
28 | this.sessionTimeout = flag;
29 | }
30 | },
31 | });
32 |
33 | // Need to be used outside the setup
34 | export function useUserStoreWithOut() {
35 | return useUserStore(store);
36 | }
37 |
--------------------------------------------------------------------------------
/src/pages/option/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
hello vue !
4 |
5 |
6 |
10 |
--------------------------------------------------------------------------------
/src/pages/option/images/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/src/pages/option/images/bg.png
--------------------------------------------------------------------------------
/src/pages/option/images/logolan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/src/pages/option/images/logolan.png
--------------------------------------------------------------------------------
/src/pages/option/images/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/src/pages/option/images/settings.png
--------------------------------------------------------------------------------
/src/pages/option/index.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 | // import store from "./store";
4 | import ElementPlus from 'element-plus'
5 | import 'element-plus/dist/index.css'
6 | let app
7 | function render() {
8 | app = createApp(App)
9 | // app.use(store)
10 | app.use(ElementPlus)
11 | app.mount('#option')
12 | }
13 | render()
--------------------------------------------------------------------------------
/src/pages/option/option.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |