├── .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 | 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 | 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 | -------------------------------------------------------------------------------- /src/pages/option/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/option/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/popup/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 10 | -------------------------------------------------------------------------------- /src/pages/popup/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/src/pages/popup/images/bg.png -------------------------------------------------------------------------------- /src/pages/popup/images/logolan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/src/pages/popup/images/logolan.png -------------------------------------------------------------------------------- /src/pages/popup/images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/src/pages/popup/images/settings.png -------------------------------------------------------------------------------- /src/pages/popup/index.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import ElementPlus from 'element-plus' 4 | import 'element-plus/dist/index.css' 5 | let app 6 | function render() { 7 | app = createApp(App) 8 | app.use(ElementPlus) 9 | app.mount('#popup') 10 | } 11 | render() -------------------------------------------------------------------------------- /src/pages/popup/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /src/pages/popup/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/popup/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/style/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f1f1f1; 3 | font-size: 14px; 4 | color: #333333; 5 | font-family: Helvetica Neue, Helvetica, sans-serif; 6 | } 7 | div, 8 | scroll-div, 9 | swiper, 10 | button, 11 | input, 12 | textarea, 13 | label, 14 | navigator, 15 | image { 16 | box-sizing: border-box; 17 | } 18 | 19 | /* -- flex弹性布局 -- */ 20 | 21 | .flex { 22 | display: flex; 23 | } 24 | 25 | .basis-xs { 26 | flex-basis: 20%; 27 | } 28 | 29 | .basis-sm { 30 | flex-basis: 40%; 31 | } 32 | 33 | .basis-df { 34 | flex-basis: 50%; 35 | } 36 | 37 | .basis-lg { 38 | flex-basis: 60%; 39 | } 40 | 41 | .basis-xl { 42 | flex-basis: 80%; 43 | } 44 | 45 | .flex-sub { 46 | flex: 1; 47 | } 48 | 49 | .flex-twice { 50 | flex: 2; 51 | } 52 | 53 | .flex-treble { 54 | flex: 3; 55 | } 56 | 57 | .flex-direction { 58 | flex-direction: column; 59 | } 60 | 61 | .flex-wrap { 62 | flex-wrap: wrap; 63 | } 64 | 65 | .align-start { 66 | align-items: flex-start; 67 | } 68 | 69 | .align-end { 70 | align-items: flex-end; 71 | } 72 | 73 | .align-center { 74 | align-items: center; 75 | } 76 | 77 | .align-stretch { 78 | align-items: stretch; 79 | } 80 | 81 | .self-start { 82 | align-self: flex-start; 83 | } 84 | 85 | .self-center { 86 | align-self: flex-center; 87 | } 88 | 89 | .self-end { 90 | align-self: flex-end; 91 | } 92 | 93 | .self-stretch { 94 | align-self: stretch; 95 | } 96 | 97 | .align-stretch { 98 | align-items: stretch; 99 | } 100 | 101 | .justify-start { 102 | justify-content: flex-start; 103 | } 104 | 105 | .justify-end { 106 | justify-content: flex-end; 107 | } 108 | 109 | .justify-center { 110 | justify-content: center; 111 | } 112 | 113 | .justify-between { 114 | justify-content: space-between; 115 | } 116 | 117 | .justify-around { 118 | justify-content: space-around; 119 | } 120 | 121 | /* -- 内外边距 -- */ 122 | 123 | .margin-0 { 124 | margin: 0; 125 | } 126 | 127 | .margin-xs { 128 | margin: 5px; 129 | } 130 | 131 | .margin-sm { 132 | margin: 10px; 133 | } 134 | 135 | .margin { 136 | margin: 15px; 137 | } 138 | 139 | .margin-lg { 140 | margin: 40px; 141 | } 142 | 143 | .margin-xl { 144 | margin: 50px; 145 | } 146 | 147 | .margin-top-xs { 148 | margin-top: 5px; 149 | } 150 | 151 | .margin-top-sm { 152 | margin-top: 10px; 153 | } 154 | 155 | .margin-top { 156 | margin-top: 15px; 157 | } 158 | 159 | .margin-top-lg { 160 | margin-top: 40px; 161 | } 162 | 163 | .margin-top-xl { 164 | margin-top: 50px; 165 | } 166 | 167 | .margin-right-xs { 168 | margin-right: 5px; 169 | } 170 | 171 | .margin-right-sm { 172 | margin-right: 10px; 173 | } 174 | 175 | .margin-right { 176 | margin-right: 15px; 177 | } 178 | 179 | .margin-right-lg { 180 | margin-right: 40px; 181 | } 182 | 183 | .margin-right-xl { 184 | margin-right: 50px; 185 | } 186 | 187 | .margin-bottom-xs { 188 | margin-bottom: 5px; 189 | } 190 | 191 | .margin-bottom-sm { 192 | margin-bottom: 10px; 193 | } 194 | 195 | .margin-bottom { 196 | margin-bottom: 15px; 197 | } 198 | 199 | .margin-bottom-lg { 200 | margin-bottom: 40px; 201 | } 202 | 203 | .margin-bottom-xl { 204 | margin-bottom: 50px; 205 | } 206 | 207 | .margin-left-xs { 208 | margin-left: 5px; 209 | } 210 | 211 | .margin-left-sm { 212 | margin-left: 10px; 213 | } 214 | 215 | .margin-left { 216 | margin-left: 15px; 217 | } 218 | 219 | .margin-left-lg { 220 | margin-left: 40px; 221 | } 222 | 223 | .margin-left-xl { 224 | margin-left: 50px; 225 | } 226 | 227 | .margin-lr-xs { 228 | margin-left: 5px; 229 | margin-right: 5px; 230 | } 231 | 232 | .margin-lr-sm { 233 | margin-left: 10px; 234 | margin-right: 10px; 235 | } 236 | 237 | .margin-lr { 238 | margin-left: 15px; 239 | margin-right: 15px; 240 | } 241 | 242 | .margin-lr-lg { 243 | margin-left: 40px; 244 | margin-right: 40px; 245 | } 246 | 247 | .margin-lr-xl { 248 | margin-left: 50px; 249 | margin-right: 50px; 250 | } 251 | 252 | .margin-tb-xs { 253 | margin-top: 5px; 254 | margin-bottom: 5px; 255 | } 256 | 257 | .margin-tb-sm { 258 | margin-top: 10px; 259 | margin-bottom: 10px; 260 | } 261 | 262 | .margin-tb { 263 | margin-top: 15px; 264 | margin-bottom: 15px; 265 | } 266 | 267 | .margin-tb-lg { 268 | margin-top: 40px; 269 | margin-bottom: 40px; 270 | } 271 | 272 | .margin-tb-xl { 273 | margin-top: 50px; 274 | margin-bottom: 50px; 275 | } 276 | 277 | .padding-0 { 278 | padding: 0; 279 | } 280 | 281 | .padding-xs { 282 | padding: 5px; 283 | } 284 | 285 | .padding-sm { 286 | padding: 10px; 287 | } 288 | 289 | .padding { 290 | padding: 15px; 291 | } 292 | 293 | .padding-lg { 294 | padding: 40px; 295 | } 296 | 297 | .padding-xl { 298 | padding: 50px; 299 | } 300 | 301 | .padding-top-xs { 302 | padding-top: 5px; 303 | } 304 | 305 | .padding-top-sm { 306 | padding-top: 10px; 307 | } 308 | 309 | .padding-top { 310 | padding-top: 15px; 311 | } 312 | 313 | .padding-top-lg { 314 | padding-top: 40px; 315 | } 316 | 317 | .padding-top-xl { 318 | padding-top: 50px; 319 | } 320 | 321 | .padding-right-xs { 322 | padding-right: 5px; 323 | } 324 | 325 | .padding-right-sm { 326 | padding-right: 10px; 327 | } 328 | 329 | .padding-right { 330 | padding-right: 15px; 331 | } 332 | 333 | .padding-right-lg { 334 | padding-right: 40px; 335 | } 336 | 337 | .padding-right-xl { 338 | padding-right: 50px; 339 | } 340 | 341 | .padding-bottom-xs { 342 | padding-bottom: 5px; 343 | } 344 | 345 | .padding-bottom-sm { 346 | padding-bottom: 10px; 347 | } 348 | 349 | .padding-bottom { 350 | padding-bottom: 15px; 351 | } 352 | 353 | .padding-bottom-lg { 354 | padding-bottom: 40px; 355 | } 356 | 357 | .padding-bottom-xl { 358 | padding-bottom: 50px; 359 | } 360 | 361 | .padding-left-xs { 362 | padding-left: 5px; 363 | } 364 | 365 | .padding-left-sm { 366 | padding-left: 10px; 367 | } 368 | 369 | .padding-left { 370 | padding-left: 15px; 371 | } 372 | 373 | .padding-left-lg { 374 | padding-left: 40px; 375 | } 376 | 377 | .padding-left-xl { 378 | padding-left: 50px; 379 | } 380 | 381 | .padding-lr-xs { 382 | padding-left: 5px; 383 | padding-right: 5px; 384 | } 385 | 386 | .padding-lr-sm { 387 | padding-left: 10px; 388 | padding-right: 10px; 389 | } 390 | 391 | .padding-lr { 392 | padding-left: 15px; 393 | padding-right: 15px; 394 | } 395 | 396 | .padding-lr-lg { 397 | padding-left: 40px; 398 | padding-right: 40px; 399 | } 400 | 401 | .padding-lr-xl { 402 | padding-left: 50px; 403 | padding-right: 50px; 404 | } 405 | 406 | .padding-tb-xs { 407 | padding-top: 5px; 408 | padding-bottom: 5px; 409 | } 410 | 411 | .padding-tb-sm { 412 | padding-top: 10px; 413 | padding-bottom: 10px; 414 | } 415 | 416 | .padding-tb { 417 | padding-top: 15px; 418 | padding-bottom: 15px; 419 | } 420 | 421 | .padding-tb-lg { 422 | padding-top: 40px; 423 | padding-bottom: 40px; 424 | } 425 | 426 | .padding-tb-xl { 427 | padding-top: 50px; 428 | padding-bottom: 50px; 429 | } 430 | 431 | .text-xs { 432 | font-size: 10px; 433 | } 434 | 435 | .text-sm { 436 | font-size: 12px; 437 | } 438 | 439 | .text-df { 440 | font-size: 14px; 441 | } 442 | 443 | .text-lg { 444 | font-size: 16px; 445 | } 446 | 447 | .text-xl { 448 | font-size: 33px; 449 | } 450 | 451 | .text-xxl { 452 | font-size: 42px; 453 | } 454 | 455 | .text-sl { 456 | font-size: 80px; 457 | } 458 | 459 | .text-xsl { 460 | font-size: 110px; 461 | } 462 | 463 | .text-Abc { 464 | text-transform: Capitalize; 465 | } 466 | 467 | .text-ABC { 468 | text-transform: Uppercase; 469 | } 470 | 471 | .text-abc { 472 | text-transform: Lowercase; 473 | } 474 | 475 | .text-price::before { 476 | content: "¥"; 477 | font-size: 80%; 478 | margin-right: 2px; 479 | } 480 | 481 | .text-cut { 482 | text-overflow: ellipsis; 483 | white-space: nowrap; 484 | overflow: hidden; 485 | } 486 | 487 | .text-bold { 488 | font-weight: bold; 489 | } 490 | 491 | .text-center { 492 | text-align: center; 493 | } 494 | 495 | .text-content { 496 | line-height: 1.6; 497 | } 498 | 499 | .text-left { 500 | text-align: left; 501 | } 502 | 503 | .text-right { 504 | text-align: right; 505 | } 506 | 507 | .text-red, 508 | .line-red, 509 | .lines-red { 510 | color: #e54d42; 511 | } 512 | 513 | .text-orange, 514 | .line-orange, 515 | .lines-orange { 516 | color: #f37b1d; 517 | } 518 | 519 | .text-yellow, 520 | .line-yellow, 521 | .lines-yellow { 522 | color: #fbbd08; 523 | } 524 | 525 | .text-olive, 526 | .line-olive, 527 | .lines-olive { 528 | color: #8dc63f; 529 | } 530 | 531 | .text-green, 532 | .line-green, 533 | .lines-green { 534 | color: #39b54a; 535 | } 536 | 537 | .text-cyan, 538 | .line-cyan, 539 | .lines-cyan { 540 | color: #1cbbb4; 541 | } 542 | 543 | .text-blue, 544 | .line-blue, 545 | .lines-blue { 546 | color: #0081ff; 547 | } 548 | 549 | .text-purple, 550 | .line-purple, 551 | .lines-purple { 552 | color: #6739b6; 553 | } 554 | 555 | .text-mauve, 556 | .line-mauve, 557 | .lines-mauve { 558 | color: #9c26b0; 559 | } 560 | 561 | .text-pink, 562 | .line-pink, 563 | .lines-pink { 564 | color: #e03997; 565 | } 566 | 567 | .text-brown, 568 | .line-brown, 569 | .lines-brown { 570 | color: #a5673f; 571 | } 572 | 573 | .text-grey, 574 | .line-grey, 575 | .lines-grey { 576 | color: #8799a3; 577 | } 578 | 579 | .text-gray, 580 | .line-gray, 581 | .lines-gray { 582 | color: #aaaaaa; 583 | } 584 | 585 | .text-black, 586 | .line-black, 587 | .lines-black { 588 | color: #333333; 589 | } 590 | 591 | .text-white, 592 | .line-white, 593 | .lines-white { 594 | color: #ffffff; 595 | } 596 | -------------------------------------------------------------------------------- /src/types/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-auto-import 5 | export {} 6 | declare global { 7 | const EffectScope: typeof import('vue')['EffectScope'] 8 | const computed: typeof import('vue')['computed'] 9 | const createApp: typeof import('vue')['createApp'] 10 | const customRef: typeof import('vue')['customRef'] 11 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 12 | const defineComponent: typeof import('vue')['defineComponent'] 13 | const effectScope: typeof import('vue')['effectScope'] 14 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 15 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 16 | const h: typeof import('vue')['h'] 17 | const inject: typeof import('vue')['inject'] 18 | const isProxy: typeof import('vue')['isProxy'] 19 | const isReactive: typeof import('vue')['isReactive'] 20 | const isReadonly: typeof import('vue')['isReadonly'] 21 | const isRef: typeof import('vue')['isRef'] 22 | const markRaw: typeof import('vue')['markRaw'] 23 | const nextTick: typeof import('vue')['nextTick'] 24 | const onActivated: typeof import('vue')['onActivated'] 25 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 26 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 27 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 28 | const onDeactivated: typeof import('vue')['onDeactivated'] 29 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 30 | const onMounted: typeof import('vue')['onMounted'] 31 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 32 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 33 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 34 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 35 | const onUnmounted: typeof import('vue')['onUnmounted'] 36 | const onUpdated: typeof import('vue')['onUpdated'] 37 | const provide: typeof import('vue')['provide'] 38 | const reactive: typeof import('vue')['reactive'] 39 | const readonly: typeof import('vue')['readonly'] 40 | const ref: typeof import('vue')['ref'] 41 | const resolveComponent: typeof import('vue')['resolveComponent'] 42 | const shallowReactive: typeof import('vue')['shallowReactive'] 43 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 44 | const shallowRef: typeof import('vue')['shallowRef'] 45 | const toRaw: typeof import('vue')['toRaw'] 46 | const toRef: typeof import('vue')['toRef'] 47 | const toRefs: typeof import('vue')['toRefs'] 48 | const triggerRef: typeof import('vue')['triggerRef'] 49 | const unref: typeof import('vue')['unref'] 50 | const useAttrs: typeof import('vue')['useAttrs'] 51 | const useCssModule: typeof import('vue')['useCssModule'] 52 | const useCssVars: typeof import('vue')['useCssVars'] 53 | const useSlots: typeof import('vue')['useSlots'] 54 | const watch: typeof import('vue')['watch'] 55 | const watchEffect: typeof import('vue')['watchEffect'] 56 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 57 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 58 | } 59 | // for type re-export 60 | declare global { 61 | // @ts-ignore 62 | export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue' 63 | } 64 | -------------------------------------------------------------------------------- /src/types/globle.d.ts: -------------------------------------------------------------------------------- 1 | declare const chrome: { 2 | [propName: string]: any 3 | } 4 | 5 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export default {}; -------------------------------------------------------------------------------- /static/img/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/static/img/bg.png -------------------------------------------------------------------------------- /static/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/static/img/icon.png -------------------------------------------------------------------------------- /static/img/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choumai555/vue-chrome-extension-mv3/784e58b20d422d72bf10b7ecfded1fa6ceaa24cc/static/img/settings.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "lib": [ 12 | "esnext", 13 | "dom" 14 | ], 15 | "baseUrl": ".", 16 | "paths": { 17 | "@/*": [ 18 | "src/*" 19 | ] 20 | } 21 | }, 22 | "include": [ 23 | "src/**/*.ts", 24 | "src/**/*.d.ts", 25 | "src/**/*.tsx", 26 | "src/**/*.vue", 27 | "*/*.vue" 28 | ] 29 | } -------------------------------------------------------------------------------- /types/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-auto-import 5 | export {} 6 | declare global { 7 | const EffectScope: typeof import('vue')['EffectScope'] 8 | const computed: typeof import('vue')['computed'] 9 | const createApp: typeof import('vue')['createApp'] 10 | const customRef: typeof import('vue')['customRef'] 11 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 12 | const defineComponent: typeof import('vue')['defineComponent'] 13 | const effectScope: typeof import('vue')['effectScope'] 14 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 15 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 16 | const h: typeof import('vue')['h'] 17 | const inject: typeof import('vue')['inject'] 18 | const isProxy: typeof import('vue')['isProxy'] 19 | const isReactive: typeof import('vue')['isReactive'] 20 | const isReadonly: typeof import('vue')['isReadonly'] 21 | const isRef: typeof import('vue')['isRef'] 22 | const markRaw: typeof import('vue')['markRaw'] 23 | const nextTick: typeof import('vue')['nextTick'] 24 | const onActivated: typeof import('vue')['onActivated'] 25 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 26 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 27 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 28 | const onDeactivated: typeof import('vue')['onDeactivated'] 29 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 30 | const onMounted: typeof import('vue')['onMounted'] 31 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 32 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 33 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 34 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 35 | const onUnmounted: typeof import('vue')['onUnmounted'] 36 | const onUpdated: typeof import('vue')['onUpdated'] 37 | const provide: typeof import('vue')['provide'] 38 | const reactive: typeof import('vue')['reactive'] 39 | const readonly: typeof import('vue')['readonly'] 40 | const ref: typeof import('vue')['ref'] 41 | const resolveComponent: typeof import('vue')['resolveComponent'] 42 | const shallowReactive: typeof import('vue')['shallowReactive'] 43 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 44 | const shallowRef: typeof import('vue')['shallowRef'] 45 | const toRaw: typeof import('vue')['toRaw'] 46 | const toRef: typeof import('vue')['toRef'] 47 | const toRefs: typeof import('vue')['toRefs'] 48 | const triggerRef: typeof import('vue')['triggerRef'] 49 | const unref: typeof import('vue')['unref'] 50 | const useAttrs: typeof import('vue')['useAttrs'] 51 | const useCssModule: typeof import('vue')['useCssModule'] 52 | const useCssVars: typeof import('vue')['useCssVars'] 53 | const useSlots: typeof import('vue')['useSlots'] 54 | const watch: typeof import('vue')['watch'] 55 | const watchEffect: typeof import('vue')['watchEffect'] 56 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 57 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 58 | } 59 | // for type re-export 60 | declare global { 61 | // @ts-ignore 62 | export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue' 63 | } 64 | -------------------------------------------------------------------------------- /types/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 | -------------------------------------------------------------------------------- /types/custom-types.d.ts: -------------------------------------------------------------------------------- 1 | import { SlateDescendant } from '@wangeditor/editor' 2 | 3 | declare module 'slate' { 4 | interface CustomTypes { 5 | // 扩展 text 6 | Text: { 7 | text: string 8 | bold?: boolean 9 | italic?: boolean 10 | code?: boolean 11 | through?: boolean 12 | underline?: boolean 13 | sup?: boolean 14 | sub?: boolean 15 | color?: string 16 | bgColor?: string 17 | fontSize?: string 18 | fontFamily?: string 19 | } 20 | 21 | // 扩展 Element 的 type 属性 22 | Element: { 23 | type: string 24 | children: SlateDescendant[] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /webpack.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve, posix } from 'path' 2 | import HtmlWebpackPlugin from 'html-webpack-plugin' 3 | import { VueLoaderPlugin } from 'vue-loader' 4 | import Components from 'unplugin-vue-components/webpack' 5 | import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' 6 | import type { Configuration } from 'webpack' 7 | import AutoImport from 'unplugin-auto-import/webpack' 8 | import webpack from 'webpack' 9 | import CopyWebpackPlugin from 'copy-webpack-plugin' 10 | function assetsPath(_path) { 11 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 12 | ? './static' 13 | : 'static' 14 | return posix.join(assetsSubDirectory, _path) 15 | } 16 | const mode: 'production' | 'development' = 17 | (process.env.MODE as any) ?? 'development' 18 | 19 | const config: Configuration = { 20 | devtool: "source-map", // 启用sourceMap 21 | mode, 22 | entry: { 23 | 'background': resolve('src', 'pages/background'), 24 | 'content': resolve('src', 'pages/content'), 25 | 'option': resolve('src', 'pages/option'), 26 | 'popup': resolve('src', 'pages/popup'), 27 | }, 28 | output: { 29 | path: resolve(__dirname, './chrome'), 30 | publicPath: './', 31 | filename: '[name].main.js' 32 | }, 33 | resolve: { 34 | alias: { 35 | '@': resolve(__dirname, 'src'), 36 | 'static': resolve('static'), 37 | }, 38 | extensions: ['.ts', '.js', '.mjs', '.json'], 39 | }, 40 | module: { 41 | rules: [ 42 | { 43 | test: /\.mjs$/i, 44 | resolve: { byDependency: { esm: { fullySpecified: false } } }, 45 | }, 46 | { test: /\.vue$/, loader: 'vue-loader' }, 47 | { 48 | test: /\.m?[tj]s$/, 49 | exclude: /node_modules/, 50 | loader: 'babel-loader', 51 | }, 52 | { 53 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 54 | loader: 'url-loader', 55 | options: { 56 | limit: 100000, 57 | name: assetsPath('img/[name].[hash:7].[ext]') 58 | } 59 | }, 60 | { test: /\.css$/, use: ['style-loader', 'css-loader'] }, 61 | { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },//less的loader 62 | { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },//scss的loader 63 | ], 64 | }, 65 | plugins: [ 66 | new webpack.ProvidePlugin({ 67 | $: 'jquery', 68 | jQuery: 'jquery' 69 | }), 70 | new VueLoaderPlugin(), 71 | new HtmlWebpackPlugin({ 72 | filename: 'popup.html', 73 | template: 'src/pages/popup/popup.html', 74 | inject: 'body', 75 | chunks: ["popup"], 76 | minify: { //压缩 77 | removeComments: true, 78 | collapseWhitespace: true, 79 | } 80 | }), 81 | new webpack.DefinePlugin({ 82 | '__VUE_OPTIONS_API__': true, 83 | '__VUE_PROD_DEVTOOLS__': false 84 | }), 85 | new HtmlWebpackPlugin({ 86 | filename: 'option.html', 87 | template: 'src/pages/option/option.html', 88 | inject: 'body', 89 | chunks: ["option"], 90 | minify: { //压缩 91 | removeComments: true, 92 | collapseWhitespace: true, 93 | } 94 | }), 95 | new CopyWebpackPlugin([{ 96 | from: resolve(__dirname, 'src/manifest.json'), 97 | to: '' 98 | }, 99 | { 100 | from: resolve(__dirname, 'static/'), 101 | to: 'static/' 102 | } 103 | ], { 104 | copyUnmodified: true 105 | }), 106 | AutoImport({ 107 | include: [ 108 | /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx 109 | /\.vue$/, 110 | /\.vue\?vue/, // .vue 111 | /\.md$/ // .md 112 | ], 113 | imports: [ 114 | 'vue', {} 115 | ], 116 | dts: 'src/types/auto-imports.d.ts', 117 | resolvers: [], 118 | eslintrc: { 119 | enabled: false, // Default `false` 120 | filepath: './.eslintrc-auto-import.json', // Default `./.eslintrc-auto-import.json` 121 | globalsPropValue: true // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable') 122 | } 123 | }), 124 | Components({ 125 | resolvers: [ElementPlusResolver({})], 126 | dts: 'types/components.d.ts', 127 | }), 128 | ], 129 | } 130 | 131 | export default config 132 | --------------------------------------------------------------------------------