liutian 2 年 前
コミット
d655779854

+ 116 - 0
.eslintrc.cjs

@@ -0,0 +1,116 @@
+/* eslint-env node */
+require("@rushstack/eslint-patch/modern-module-resolution");
+
+module.exports = {
+  root: true,
+  env: {
+    browser: true,
+    node: true,
+  },
+  extends: [
+    "plugin:vue/essential",
+    "eslint:recommended"
+  ],
+  parser: "vue-eslint-parser",
+  rules: {
+    "semi": ["error", "never"],
+    "no-unused-vars": "off",
+    "no-undef": "off",
+    "@typescript-eslint/no-unused-vars": "off",
+    "vue/max-attributes-per-line": "off",
+    "vue/no-v-html": "off",
+    "vue/require-prop-types": "off",
+    "vue/require-default-prop": "off",
+    "vue/multi-word-component-names": "off",
+    "vue/prefer-import-from-vue": "off",
+
+    // reactivity transform
+    "vue/no-setup-props-destructure": "off",
+
+    "vue/component-tags-order": [
+      "error",
+      {
+        order: ["script", "template", "style"],
+      },
+    ],
+    "vue/block-tag-newline": [
+      "error",
+      {
+        singleline: "always",
+        multiline: "always",
+      },
+    ],
+    "vue/component-name-in-template-casing": ["error", "PascalCase"],
+    "vue/component-options-name-casing": ["error", "PascalCase"],
+    "vue/custom-event-name-casing": ["error", "camelCase"],
+    "vue/define-macros-order": [
+      "error",
+      {
+        order: ["defineProps", "defineEmits"],
+      },
+    ],
+    "vue/html-comment-content-spacing": [
+      "error",
+      "always",
+      {
+        exceptions: ["-"],
+      },
+    ],
+    "vue/no-restricted-v-bind": ["error", "/^v-/"],
+    "vue/no-useless-v-bind": "error",
+    "vue/no-v-text-v-html-on-component": "error",
+    "vue/padding-line-between-blocks": ["error", "always"],
+    "vue/prefer-separate-static-class": "error",
+
+    // extensions
+    "vue/array-bracket-spacing": ["error", "never"],
+    "vue/arrow-spacing": ["error", { before: true, after: true }],
+    "vue/block-spacing": ["error", "always"],
+    "vue/brace-style": ["error", "stroustrup", { allowSingleLine: true }],
+    "vue/comma-dangle": ["error", "always-multiline"],
+    "vue/comma-spacing": ["error", { before: false, after: true }],
+    "vue/comma-style": ["error", "last"],
+    "vue/dot-location": ["error", "property"],
+    "vue/dot-notation": ["error", { allowKeywords: true }],
+    "vue/eqeqeq": ["error", "smart"],
+    // 'vue/func-call-spacing': ['off', 'never'],
+    "vue/key-spacing": ["error", { beforeColon: false, afterColon: true }],
+    "vue/keyword-spacing": ["error", { before: true, after: true }],
+    "vue/no-constant-condition": "warn",
+    "vue/no-empty-pattern": "error",
+    "vue/no-extra-parens": ["error", "functions"],
+    "vue/no-irregular-whitespace": "error",
+    "vue/no-loss-of-precision": "error",
+    "vue/no-restricted-syntax": [
+      "error",
+      "DebuggerStatement",
+      "LabeledStatement",
+      "WithStatement",
+    ],
+    "vue/no-sparse-arrays": "error",
+    "vue/object-curly-newline": [
+      "error",
+      { multiline: true, consistent: true },
+    ],
+    "vue/object-curly-spacing": ["error", "always"],
+    "vue/object-property-newline": [
+      "error",
+      { allowMultiplePropertiesPerLine: true },
+    ],
+    "vue/object-shorthand": [
+      "error",
+      "always",
+      {
+        ignoreConstructors: false,
+        avoidQuotes: true,
+      },
+    ],
+    "vue/operator-linebreak": ["error", "before"],
+    "vue/prefer-template": "error",
+    "vue/quote-props": ["error", "consistent-as-needed"],
+    "vue/space-in-parens": ["error", "never"],
+    "vue/space-infix-ops": "error",
+    "vue/space-unary-ops": ["error", { words: true, nonwords: false }],
+    "vue/template-curly-spacing": "error",
+  },
+};

+ 27 - 0
.gitignore

@@ -0,0 +1,27 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 48 - 0
README.md

@@ -0,0 +1,48 @@
+# pdf_tech_front_end
+
+This template should help get you started developing with Vue 3 in Vite.
+
+## Recommended IDE Setup
+
+[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
+
+## Customize configuration
+
+See [Vite Configuration Reference](https://vitejs.dev/config/).
+
+## Project Setup
+
+```sh
+pnpm install
+```
+
+### Compile and Hot-Reload for Development
+
+```sh
+pnpm run dev
+```
+
+### Compile and Minify for Production
+
+```sh
+pnpm run build
+```
+
+### Run Unit Tests with [Vitest](https://vitest.dev/)
+
+```sh
+pnpm run test:unit
+```
+
+### Run End-to-End Tests with [Cypress](https://www.cypress.io/)
+
+```sh
+pnpm run build
+pnpm run test:e2e # or `npm run test:e2e:ci` for headless testing
+```
+
+### Lint with [ESLint](https://eslint.org/)
+
+```sh
+pnpm run lint
+```

+ 8 - 0
cypress.config.js

@@ -0,0 +1,8 @@
+const { defineConfig } = require("cypress");
+
+module.exports = defineConfig({
+  e2e: {
+    specPattern: "cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}",
+    baseUrl: "http://localhost:4173",
+  },
+});

+ 8 - 0
cypress/e2e/example.cy.js

@@ -0,0 +1,8 @@
+// https://docs.cypress.io/api/introduction/api.html
+
+describe("My First Test", () => {
+  it("visits the app root url", () => {
+    cy.visit("/");
+    cy.contains("h1", "You did it!");
+  });
+});

+ 8 - 0
cypress/e2e/jsconfig.json

@@ -0,0 +1,8 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "lib": ["es5", "dom"],
+    "types": ["cypress"]
+  },
+  "include": ["./**/*", "../support/**/*"]
+}

+ 5 - 0
cypress/fixtures/example.json

@@ -0,0 +1,5 @@
+{
+  "name": "Using fixtures to represent data",
+  "email": "hello@cypress.io",
+  "body": "Fixtures are a great way to mock data for responses to routes"
+}

+ 25 - 0
cypress/support/commands.js

@@ -0,0 +1,25 @@
+// ***********************************************
+// This example commands.js shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+// Cypress.Commands.add('login', (email, password) => { ... })
+//
+//
+// -- This is a child command --
+// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

+ 20 - 0
cypress/support/e2e.js

@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/index.js is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import "./commands";
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>PDF Tech</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 38 - 0
package.json

@@ -0,0 +1,38 @@
+{
+  "name": "pdf_tech_front_end",
+  "version": "0.0.0",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview --port 4173",
+    "test:unit": "vitest --environment jsdom",
+    "test:e2e": "start-server-and-test preview http://localhost:4173/ 'cypress open --e2e'",
+    "test:e2e:ci": "start-server-and-test preview http://localhost:4173/ 'cypress run --e2e'",
+    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
+  },
+  "dependencies": {
+    "axios": "^1.2.1",
+    "pinia": "^2.0.16",
+    "vue": "^2.7.7",
+    "vue-router": "^3.5.4"
+  },
+  "devDependencies": {
+    "@rushstack/eslint-patch": "^1.1.0",
+    "@vitejs/plugin-legacy": "^2.0.0",
+    "@vitejs/plugin-vue2": "^1.1.2",
+    "@vue/eslint-config-prettier": "^7.0.0",
+    "@vue/test-utils": "^1.3.0",
+    "cypress": "^10.3.0",
+    "eslint": "^8.5.0",
+    "eslint-plugin-cypress": "^2.12.1",
+    "eslint-plugin-vue": "^9.0.0",
+    "jsdom": "^20.0.0",
+    "prettier": "^2.5.1",
+    "start-server-and-test": "^1.14.0",
+    "terser": "^5.14.2",
+    "vite": "^3.0.2",
+    "vite-plugin-eslint": "^1.8.1",
+    "vitest": "^0.18.1",
+    "vue-template-compiler": "^2.7.7"
+  }
+}

ファイルの差分が大きいため隠しています
+ 3103 - 0
pnpm-lock.yaml


+ 6 - 0
src/App.vue

@@ -0,0 +1,6 @@
+<template>
+  <div id="app">
+    <router-view />
+  </div>
+</template>
+

+ 6 - 0
src/assets/base.css

@@ -0,0 +1,6 @@
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+  margin: 0;
+}

+ 13 - 0
src/main.js

@@ -0,0 +1,13 @@
+import Vue from "vue"
+import { createPinia, PiniaVuePlugin } from "pinia"
+
+import App from "./App.vue"
+import router from "./router"
+
+Vue.use(PiniaVuePlugin)
+
+new Vue({
+  router,
+  pinia: createPinia(),
+  render: (h) => h(App),
+}).$mount("#app")

+ 18 - 0
src/router/index.js

@@ -0,0 +1,18 @@
+import Vue from "vue"
+import VueRouter from "vue-router"
+
+Vue.use(VueRouter)
+
+const router = new VueRouter({
+  mode: "history",
+  base: import.meta.env.BASE_URL,
+  routes: [
+    {
+      path: "/",
+      name: "home",
+      component: () => import("../views/HomeView.vue"),
+    },
+  ],
+})
+
+export default router

+ 16 - 0
src/stores/counter.js

@@ -0,0 +1,16 @@
+import { defineStore } from "pinia"
+
+export const useCounterStore = defineStore({
+  id: "counter",
+  state: () => ({
+    counter: 0,
+  }),
+  getters: {
+    doubleCount: (state) => state.counter * 2,
+  },
+  actions: {
+    increment() {
+      this.counter++
+    },
+  },
+})

+ 7 - 0
src/views/HomeView.vue

@@ -0,0 +1,7 @@
+<script setup>
+
+</script>
+
+<template>
+  <div>Welcome</div>
+</template>

+ 5 - 0
tsconfig.json

@@ -0,0 +1,5 @@
+{
+  "compilerOptions": {
+    "allowJs": true
+  }
+}

+ 103 - 0
utils/request.js

@@ -0,0 +1,103 @@
+import axios from 'axios';
+
+// 创建请求实例
+const instance = axios.create({
+  baseURL: '/api',
+  // 指定请求超时的毫秒数
+  timeout: 1000,
+  // 表示跨域请求时是否需要使用凭证
+  withCredentials: false,
+});
+
+// 前置拦截器(发起请求之前的拦截)
+instance.interceptors.request.use(
+  (config) => {
+    /**
+     * 在这里一般会携带前台的参数发送给后台,比如下面这段代码:
+     * const token = getToken()
+     * if (token) {
+     *  config.headers.token = token
+     * }
+     */
+    return config;
+  },
+  (error) => {
+    return Promise.reject(error);
+  },
+);
+
+// 后置拦截器(获取到响应时的拦截)
+instance.interceptors.response.use(
+  (response) => {
+    /**
+     * 根据你的项目实际情况来对 response 和 error 做处理
+     * 这里对 response 和 error 不做任何处理,直接返回
+     */
+    return response;
+  },
+  (error) => {
+    const { response } = error;
+    if (response && response.data) {
+      return Promise.reject(error);
+    }
+    const { message } = error;
+    console.error(message);
+    return Promise.reject(error);
+  },
+);
+
+// 导出常用函数
+
+/**
+ * @param {string} url
+ * @param {object} data
+ * @param {object} params
+ */
+export function post(url, data = {}, params = {}) {
+  return instance({
+    method: 'post',
+    url,
+    data,
+    params,
+  });
+}
+
+/**
+ * @param {string} url
+ * @param {object} params
+ */
+export function get(url, params = {}) {
+  return instance({
+    method: 'get',
+    url,
+    params,
+  });
+}
+
+/**
+ * @param {string} url
+ * @param {object} data
+ * @param {object} params
+ */
+export function put(url, data = {}, params = {}) {
+  return instance({
+    method: 'put',
+    url,
+    params,
+    data,
+  });
+}
+
+/**
+ * @param {string} url
+ * @param {object} params
+ */
+export function _delete(url, params = {}) {
+  return instance({
+    method: 'delete',
+    url,
+    params,
+  });
+}
+
+export default instance;

+ 46 - 0
vite.config.js

@@ -0,0 +1,46 @@
+import { fileURLToPath, URL } from "node:url";
+import eslintPlugin from 'vite-plugin-eslint';
+import path from "path"
+
+import { defineConfig } from "vite";
+import legacy from "@vitejs/plugin-legacy";
+import vue2 from "@vitejs/plugin-vue2";
+console.log(import.meta.url)
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [
+    vue2(),
+    legacy({
+      targets: ["ie >= 11"],
+      additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
+    }),
+    eslintPlugin({
+      include: ['src/**/*.js', 'src/**/*.vue', 'src/*.js', 'src/*.vue'],
+    }),
+  ],
+  resolve: {
+    alias: {
+      "@": path.resolve(__dirname, 'src'),
+    },
+  },
+  server: {
+    // 是否开启 https
+    https: false,
+    // 端口号
+    port: 3000,
+    // 监听所有地址
+    host: "0.0.0.0",
+    // 服务启动时是否自动打开浏览器
+    open: true,
+  },
+  build: {
+    // 设置最终构建的浏览器兼容目标
+    target: "es2015",
+    // 构建后是否生成 source map 文件
+    sourcemap: false,
+    //  chunk 大小警告的限制(以 kbs 为单位)
+    chunkSizeWarningLimit: 2000,
+    // 启用/禁用 gzip 压缩大小报告
+    reportCompressedSize: false,
+  },
+});