coderDJing 1 год назад
Сommit
4c00efe404

+ 9 - 0
.editorconfig

@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true

+ 1 - 0
.env

@@ -0,0 +1 @@
+VITE_APP_TITLE = ''

+ 1 - 0
.github/FUNDING.yml

@@ -0,0 +1 @@
+github: antfu

+ 47 - 0
.github/workflows/test.yml

@@ -0,0 +1,47 @@
+name: Test
+
+on:
+  push:
+    branches:
+      - main
+
+  pull_request:
+    branches:
+      - main
+
+jobs:
+  build:
+    runs-on: ${{ matrix.os }}
+
+    timeout-minutes: 10
+
+    strategy:
+      matrix:
+        node_version: [18.x]
+        os: [ubuntu-latest, windows-latest]
+      fail-fast: false
+
+    steps:
+      - uses: actions/checkout@v3
+      - uses: pnpm/action-setup@v2
+
+      - name: Set node version to ${{ matrix.node_version }}
+        uses: actions/setup-node@v3
+        with:
+          node-version: ${{ matrix.node_version }}
+          cache: pnpm
+
+      - name: Install
+        run: pnpm i
+
+      - name: Build
+        run: pnpm run build
+
+      - name: Test
+        run: pnpm run test
+
+      - name: Lint
+        run: pnpm run lint
+
+      - name: TypeCheck
+        run: pnpm run typecheck

+ 12 - 0
.gitignore

@@ -0,0 +1,12 @@
+.DS_Store
+.vite-ssg-dist
+.vite-ssg-temp
+*.local
+dist
+dist-ssr
+node_modules
+.idea/
+*.log
+
+components.d.ts
+auto-imports.d.ts

+ 2 - 0
.npmrc

@@ -0,0 +1,2 @@
+shamefully-hoist=true
+strict-peer-dependencies=false

+ 11 - 0
.vscode/extensions.json

@@ -0,0 +1,11 @@
+{
+  "recommendations": [
+    "antfu.vite",
+    "antfu.iconify",
+    "antfu.unocss",
+    "antfu.goto-alias",
+    "vue.volar",
+    "dbaeumer.vscode-eslint",
+    "EditorConfig.EditorConfig"
+  ]
+}

+ 12 - 0
.vscode/settings.json

@@ -0,0 +1,12 @@
+{
+  "cSpell.words": ["Vitesse"],
+  "prettier.enable": false,
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": "explicit"
+  },
+  "files.associations": {
+    "*.css": "postcss"
+  },
+  "editor.formatOnSave": false,
+  "vue.codeActions.enabled": false
+}

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020-PRESENT Anthony Fu
+
+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.

+ 87 - 0
README.zh-CN.md

@@ -0,0 +1,87 @@
+<p align='center'>
+  <img src='https://user-images.githubusercontent.com/11247099/111864893-a457fd00-899e-11eb-9f05-f4b88987541d.png' alt='Vitesse - Opinionated Vite Starter Template' width='600'/>
+</p>
+
+<h6 align='center'>
+<a href="https://vitesse-lite.netlify.app/">在线 Demo</a>
+</h6>
+
+<h5 align='center'>
+<b>轻量版的 <a href="https://github.com/antfu/vitesse">Vitesse</a></b>
+</h5>
+
+<br>
+
+<p align='center'>
+<a href="https://github.com/antfu/vitesse-lite/blob/main/README.md">English</a> | <b>简体中文</b>
+</p>
+
+<br>
+
+## 特性
+
+- ⚡️ [Vue 3](https://github.com/vuejs/core), [Vite 3](https://github.com/vitejs/vite), [pnpm](https://pnpm.io/), [ESBuild](https://github.com/evanw/esbuild) - 就是快!
+
+- 🗂 [基于文件的路由](./src/pages)
+
+- 📦 [组件自动化加载](./src/components)
+
+- 🎨 [UnoCSS](https://github.com/unocss/unocss) - 高性能且极具灵活性的即时原子化 CSS 引擎
+
+- 😃 [各种图标集为你所用](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
+
+- 🔥 使用 [新的 `<script setup>` 语法](https://github.com/vuejs/rfcs/pull/227)
+
+- ✅ 使用 [Vitest](http://vitest.dev/) 进行单元和组件测试
+
+- 🦾 TypeScript, 当然
+
+- ☁️ 零配置部署 Netlify
+
+<br>
+
+完整特性,请查看 [Vitesse](https://github.com/antfu/vitesse)
+
+## 从 [Vitesse](https://github.com/antfu/vitesse) 中删除了以下特性
+
+- ~~i18n~~
+- ~~Layouts~~
+- ~~SSG~~
+- ~~PWA~~
+- ~~Markdown~~
+
+## 预配置
+
+### UI 框架
+
+- [UnoCSS](https://github.com/antfu/unocss) - 高性能且极具灵活性的即时原子化 CSS 引擎
+
+### Icons
+
+- [Iconify](https://iconify.design) - 使用任意的图标集,浏览:[🔍Icônes](https://icones.netlify.app/)
+- [UnoCSS 的纯 CSS 图标方案](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
+
+### 插件
+
+- [Vue Router](https://github.com/vuejs/vue-router)
+  - [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) - 以文件系统为基础的路由
+- [`unplugin-auto-import`](https://github.com/antfu/unplugin-auto-import) - 直接使用 Composition API 等,无需导入
+- [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components) - 自动加载组件
+- [`unplugin-vue-macros`](https://github.com/sxzz/unplugin-vue-macros) - 探索并扩展更多的宏和语法糖到 Vue 中
+- [VueUse](https://github.com/antfu/vueuse) - 实用的 Composition API 工具合集
+
+## 现在可以试试!
+
+### GitHub 模板
+
+[使用这个模板创建仓库](https://github.com/antfu/vitesse-lite/generate).
+
+### 克隆到本地
+
+如果您更喜欢使用更干净的 git 历史记录手动执行此操作
+
+```bash
+npx degit antfu/vitesse-lite my-vitesse-app
+cd my-vitesse-app
+pnpm i # 如果你没装过 pnpm, 可以先运行: npm install -g pnpm
+```

+ 36 - 0
index.html

@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <link rel="icon" href="/favicon.svg" type="image/svg+xml">
+  <title></title>
+  <meta name="description" content="Opinionated Vite Starter Template">
+  <!-- <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script> -->
+</head>
+
+<body class="font-sans dark:text-white dark:bg-hex-121212">
+  <div id="app"></div>
+  <noscript>
+    <div>Please enable JavaScript to use this application.</div>
+  </noscript>
+  <script>(function () {
+      // var vConsole = new window.VConsole();
+      // const angent = navigator.userAgent
+      // const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
+      // const setting = localStorage.getItem('color-schema') || 'auto'
+      // if (setting === 'dark' || (prefersDark && setting !== 'light'))
+      //     document.documentElement.classList.toggle('dark', true)
+
+  document.getElementsByTagName('html')[0].style.fontSize = `${(document.documentElement.clientWidth / 750) * 100}px`
+  window.onresize = function () {
+          document.getElementsByTagName('html')[0].style.fontSize = `${(document.documentElement.clientWidth / 750) * 100}px`
+  }
+})()
+  </script>
+  <script src="/config.js"></script>
+  <script type="module" src="/src/main.ts"></script>
+</body>
+
+</html>

+ 12 - 0
netlify.toml

@@ -0,0 +1,12 @@
+[build.environment]
+  NPM_FLAGS = "--version"
+  NODE_VERSION = "16"
+
+[build]
+  publish = "dist"
+  command = "npx pnpm i --store=node_modules/.pnpm-store && npx pnpm run build"
+
+[[redirects]]
+  from = "/*"
+  to = "/index.html"
+  status = 200

Разница между файлами не показана из-за своего большого размера
+ 21521 - 0
package-lock.json


+ 60 - 0
package.json

@@ -0,0 +1,60 @@
+{
+  "private": true,
+  "packageManager": "pnpm@8.5.1",
+  "scripts": {
+    "build": "vite build",
+    "dev": "vite --port 3333 --open",
+    "typecheck": "vue-tsc --noEmit",
+    "preview": "vite preview",
+    "test": "vitest",
+    "up": "taze major -I",
+    "postinstall": "npx simple-git-hooks"
+  },
+  "dependencies": {
+    "@vueuse/core": "^10.1.2",
+    "amfe-flexible": "^2.2.1",
+    "autoprefixer": "^10.4.14",
+    "axios": "^1.4.0",
+    "lib-flexible": "^0.3.2",
+    "pdfh5": "^1.4.3",
+    "pinia": "^2.1.1",
+    "pinia-plugin-persistedstate": "^3.1.0",
+    "postcss-pxtorem": "^6.0.0",
+    "sass": "^1.62.1",
+    "vant": "^4.4.1",
+    "vconsole": "^3.15.1",
+    "vue": "^3.3.2",
+    "vue-router": "^4.2.0"
+  },
+  "devDependencies": {
+    "@antfu/eslint-config": "^0.38.6",
+    "@iconify-json/carbon": "^1.1.16",
+    "@types/file-saver": "^2.0.5",
+    "@types/node": "^18.16.11",
+    "@vitejs/plugin-legacy": "^4.0.5",
+    "@vitejs/plugin-vue": "^4.2.3",
+    "@vue-macros/volar": "^0.9.8",
+    "@vue/test-utils": "^2.3.2",
+    "jsdom": "^22.0.0",
+    "pnpm": "^8.5.1",
+    "simple-git-hooks": "^2.8.1",
+    "taze": "^0.10.1",
+    "typescript": "^5.0.4",
+    "unplugin-auto-import": "^0.16.0",
+    "unplugin-vue-components": "^0.24.1",
+    "unplugin-vue-macros": "^2.1.7",
+    "vite": "^4.3.7",
+    "vite-plugin-pages": "^0.29.0",
+    "vite-plugin-windicss": "^1.9.0",
+    "vitest": "^0.31.0",
+    "vue-tsc": "^1.6.5",
+    "windicss": "^3.5.6"
+  },
+  "simple-git-hooks": {},
+  "lint-staged": {},
+  "eslintConfig": {
+    "extends": [
+      "@antfu"
+    ]
+  }
+}

Разница между файлами не показана из-за своего большого размера
+ 9691 - 0
pnpm-lock.yaml


+ 9 - 0
postcss.config.js

@@ -0,0 +1,9 @@
+// postcss.config.js
+module.exports = {
+  plugins: {
+    'postcss-pxtorem': {
+      rootValue: 37.5,
+      propList: ['*'],
+    },
+  },
+}

+ 45 - 0
public/config.js

@@ -0,0 +1,45 @@
+// 本地开发环境
+const local = {
+  // web_pc: 'https://tyyx.bozedu.top/',
+  // web_mobile: 'https://tyyxm.bozedu.top/',
+
+  api: 'https://txwxopenapi.bozedu.top/',
+  oss: 'https://txwxopenapi.bozedu.top/',
+  uc: 'https://uc.dev.bozedu.net/',
+  origin: window.location.origin,
+  tyyx: 'https://tyyx.bozedu.top/#/home'
+}
+
+// 测试服环境
+const development = {
+  web_pc: 'https://txwx.bozedu.top/',
+
+  origin: window.location.origin,
+  api: 'https://txwxopenapi.bozedu.top/',
+  oss: 'https://txwxopenapi.bozedu.top/',
+  uc: 'https://uc.dev.bozedu.net/',
+  tyyx: 'https://tyyx.bozedu.top/#/home'
+}
+
+// // 正式服环境
+const production = {
+  web_pc: 'https://txwx.txhlwxx.com/',
+
+  origin: window.location.origin,
+  api: 'https://txwxopenapi.txhlwxx.com/',
+  oss: 'https://txwxopenapi.txhlwxx.com/',
+  uc: 'https://uc.bozedu.net/',
+  tyyx:'https://tyyx.txhlwxx.com/#/home'
+}
+
+function isWhich() {
+  if (window.location.href.startsWith(development.web_pc)) {
+      return production
+  } else if (window.location.href.startsWith(production.web_pc)) {
+      return production
+  } else {
+      return production;
+  }
+}
+
+window.GLOBAL_CONFIG = isWhich()

+ 6 - 0
shims.d.ts

@@ -0,0 +1,6 @@
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue'
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}
+declare module 'vite-plugin-windicss'

+ 7 - 0
src/App.vue

@@ -0,0 +1,7 @@
+<template>
+  <main>
+    <Suspense>
+      <RouterView style="background-color: white;min-height: 100vh;" />
+    </Suspense>
+  </main>
+</template>

+ 9 - 0
src/components/README.md

@@ -0,0 +1,9 @@
+## Components
+
+Components in this dir will be auto-registered and on-demand, powered by [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components).
+
+### Icons
+
+You can use icons from almost any icon sets by the power of [Iconify](https://iconify.design/).
+
+It will only bundle the icons you use. Check out [`unplugin-icons`](https://github.com/antfu/unplugin-icons) for more details.

+ 2 - 0
src/composables/dark.ts

@@ -0,0 +1,2 @@
+export const isDark = useDark()
+export const toggleDark = useToggle(isDark)

+ 1 - 0
src/composables/index.ts

@@ -0,0 +1 @@
+export * from './dark'

+ 19 - 0
src/main.ts

@@ -0,0 +1,19 @@
+import { createApp } from 'vue'
+import router from './router/index'
+import { ImagePreview } from 'vant';
+import App from './App.vue'
+import 'vant/es/toast/style'
+import 'vant/es/dialog/style'
+import 'vant/lib/index.css'
+
+
+
+import 'amfe-flexible'
+
+import 'virtual:windi.css'
+import './styles/main.scss'
+
+const app = createApp(App)
+app.use(router)
+app.mount('#app')
+app.use(ImagePreview)

+ 20 - 0
src/pages/README.md

@@ -0,0 +1,20 @@
+## File-based Routing
+
+Routes will be auto-generated for Vue files in this dir with the same file structure.
+Check out [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) for more details.
+
+### Path Aliasing
+
+`~/` is aliased to `./src/` folder.
+
+For example, instead of having
+
+```ts
+import { isDark } from '../../../../composables'
+```
+
+now, you can use
+
+```ts
+import { isDark } from '~/composables'
+```

+ 5 - 0
src/pages/[...all].vue

@@ -0,0 +1,5 @@
+<template>
+  <div>
+    Not Found
+  </div>
+</template>

+ 13 - 0
src/pages/index.vue

@@ -0,0 +1,13 @@
+<script setup>
+defineOptions({
+  name: 'IndexPage',
+})
+</script>
+
+<template>
+
+</template>
+
+<style lang="scss" scoped>
+
+</style>

+ 161 - 0
src/request/request.ts

@@ -0,0 +1,161 @@
+import axios from 'axios'
+import { showFailToast } from 'vant'
+
+import { user } from '~/store'
+
+let token = user?.token;
+
+const service = axios.create({
+  baseURL: window.globalVariables.api,
+  method: 'post',
+  timeout: 60000,
+  headers: {
+    'Content-Type': 'application/x-www-form-urlencoded',
+  },
+})
+
+service.interceptors.request.use(
+  (config) => {
+    // do something before request is sent
+
+    config.data = Object.assign(
+      {
+        token,
+        site: 'jnmf',
+        client: 'web',
+        api: 'json',
+        // from: 'backend',
+        issubmit: (config.url.endsWith('add') || config.url.endsWith('edit')) ? 1 : undefined,
+      },
+      config.data || {},
+    )
+    if (config.method === 'get') {
+      config.params = Object.assign(
+        {
+          token,
+        },
+        config.params || {},
+      )
+    }
+    // 短时间多个请求会冲突
+    // console.groupCollapsed('axios: ' + config.url)
+    // console.log('request', config.data)
+    return config
+  },
+  (error) => {
+    // do something with request error
+    console.log(error) // for debug
+    return Promise.reject(error)
+  },
+)
+
+// response interceptor
+let isRelogin = false
+service.interceptors.response.use(
+  (response, c) => {
+    const { code, msg, data } = response.data
+    if (code === '10001') {
+      if (!isRelogin) {
+        isRelogin = true
+        showFailToast(msg)
+        request({
+          url: '/user/main/login',
+          data: {
+            token: data.token,
+          },
+        }).then((res) => {
+          if (res.code == 1) {
+            localStorage.setItem('userInfo', JSON.stringify(res.data))
+            isRelogin = false
+            // 视情况打开
+            // location.reload()
+          }
+        })
+      }
+    }
+    else if (code !== '1') {
+      showFailToast(msg)
+    }
+    // console.log('response', response.data)
+    // console.groupEnd()
+    return response.data
+  },
+  (error) => {
+    console.error(`err${error}`) // for debug
+    return Promise.reject(error)
+  },
+)
+
+// 上传
+export const request = axios.create({
+  baseURL: window.globalVariables.api,
+  method: 'post',
+  timeout: 5000,
+  headers: {
+    'Content-Type': 'application/x-www-form-urlencoded',
+  },
+})
+request.interceptors.response.use(
+  (response) => {
+    const { code, msg } = response.data
+    if (code !== '1')
+      showFailToast(msg)
+
+    return response.data
+  },
+  (error) => {
+    console.log(`err${error}`) // for debug
+    return Promise.reject(error)
+  },
+)
+
+
+const obj2form = (data) => {
+  const formData = new FormData()
+  Object.keys(data).forEach(key => formData.append(key, data[key]))
+  return formData;
+}
+export const REQUEST = {
+  empty: axios,
+  default: service,
+  import: (c) => service({
+    timeout: 10 * 60 * 1000,
+    transformRequest: [obj2form],
+    ...c
+  }),
+  upload: (c) => service({
+    timeout: 3 * 60 * 1000,
+    transformRequest: [obj2form],
+    ...c
+  }),
+  download: (c) => service({
+    timeout: 1 * 60 * 1000,
+    method: 'get',
+    params: { token, limit: 10000, page: 1, api: 'xls', ...c }
+  })
+}
+
+export function download(url, data, name) {
+  const params = Object.assign({ token, limit: 10000, page: 1, api: 'xls' }, data)
+  const paramsStr = Object.entries(params).map(([k, v]) => `${k}=${v}`).join('&')
+  const el = document.createElement('a')
+  const href = `${window.globalVariables.api}${url}?${paramsStr}`
+  el.setAttribute('href', href)
+  name && el.setAttribute('download', name)
+  el.click()
+}
+
+export function download2(url, name) {
+  const el = document.createElement('a')
+  const href = `${window.globalVariables.api}/openapi/download.php?path=${url}&name=${name}`
+  el.setAttribute('href', href)
+  name && el.setAttribute('download', name)
+  el.click()
+}
+
+export default (...args) => {
+  if (!isRelogin)
+    return service(...args)
+  else
+    return Promise.reject({})
+}

+ 16 - 0
src/router/index.ts

@@ -0,0 +1,16 @@
+import { createRouter, createWebHashHistory } from 'vue-router'
+import routes from 'virtual:generated-pages'
+
+const router = createRouter({
+  history: createWebHashHistory(import.meta.env.BASE_URL),
+  routes,
+})
+
+router.beforeEach((to, from) => {
+  // if (user.value?.token)
+  //   return true
+  // if (!user.value?.token && to.name !== 'login')
+  //   return { name: 'login' }
+})
+
+export default router

Разница между файлами не показана из-за своего большого размера
+ 24 - 0
src/store/index.ts


Разница между файлами не показана из-за своего большого размера
+ 60 - 0
src/store/user.ts


+ 82 - 0
src/store/user/index.d.ts

@@ -0,0 +1,82 @@
+export interface User {
+  about: string
+  address: string
+  area_id1: string
+  area_id2: string
+  area_id3: string
+  area_id4: string
+  create_dateline: string
+  email_token: string
+  email_token_exptime: string
+  email_validation: string
+  gender: string
+  ischeck: string
+  isdelete: string
+  modify_dateline: string
+  openid: string
+  parent_user_id: string
+  phone_validation: string
+  qq: string
+  setting: Setting
+  /**
+   * 用户token
+   */
+  token: string
+  user_abalance: string
+  user_address: { [key: string]: any }
+  user_alipay_account: string
+  /**
+   * 用户头像
+   */
+  user_avatar: string
+  user_birthday: string
+  user_cardno: string
+  user_dcash: string
+  user_detail_id: string
+  user_dqsj: string
+  /**
+   * email
+   */
+  user_email: string
+  user_group_id: string
+  user_id: string
+  user_level: string
+  user_level_id: string
+  /**
+   * 用户名
+   */
+  user_name: string
+  /**
+   * 用户昵称
+   */
+  user_nickname: string
+  user_password: string
+  user_phone: string
+  /**
+   * 用户真实姓名
+   */
+  user_realname: string
+  user_role_id: string
+  user_score: string
+  webchatopenid: string,
+  user_role_id_ext_now: string,
+}
+
+interface Setting {
+  site_address: string
+  site_email: string
+  site_hotphone: string
+  site_icp: string
+  site_linephone: string
+  site_wx_appid: string
+  site_wx_mchid: string
+  site_wx_sp_appid: string
+  site_wx_sp_mchid: string
+  site_wxapp_appid: string
+  sitedomain: string
+  sitefavicon: string
+  sitelogo: string
+  sitename: string
+  siteqrcode: string
+  user_failedlogin_max: string
+}

+ 28 - 0
src/styles/main.scss

@@ -0,0 +1,28 @@
+html,
+body,
+#app {
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  // background: #ffff;
+  background-color: #f2f2f2;
+}
+
+body {
+  // padding-top: 1vh;
+  background-color: #f2f2f2;
+}
+
+html.dark {
+  background: #121212;
+}
+
+.border_bottom {
+  border-bottom: 2px solid #666;
+  border-top: 2px solid #666;
+}
+
+// 解决苹果手机点击input框放大的问题
+input {
+  font-size: 16px !important;
+}

+ 3 - 0
src/utils/getAssets.js

@@ -0,0 +1,3 @@
+export function getAssetsImages(name) {
+  return new URL(`/src/assets/${name}`, import.meta.url).href
+}

+ 3 - 0
test/__snapshots__/component.test.ts.snap

@@ -0,0 +1,3 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`TheCounter.vue > should render 1`] = `"<div>10 <button class=\\"inc\\"> + </button><button class=\\"dec\\"> - </button></div>"`;

+ 7 - 0
test/basic.test.ts

@@ -0,0 +1,7 @@
+import { describe, expect, it } from 'vitest'
+
+describe('Hi', () => {
+  it('should works', () => {
+    expect(1 + 1).toEqual(2)
+  })
+})

+ 22 - 0
test/component.test.ts

@@ -0,0 +1,22 @@
+import { mount } from '@vue/test-utils'
+import { describe, expect, it } from 'vitest'
+import TheCounter from '../src/components/TheCounter.vue'
+
+describe('TheCounter.vue', () => {
+  it('should render', () => {
+    const wrapper = mount(TheCounter, { props: { initial: 10 } })
+    expect(wrapper.text()).toContain('10')
+    expect(wrapper.html()).toMatchSnapshot()
+  })
+
+  it('should be interactive', async () => {
+    const wrapper = mount(TheCounter, { props: { initial: 0 } })
+    expect(wrapper.text()).toContain('0')
+
+    expect(wrapper.find('.inc').exists()).toBe(true)
+
+    await wrapper.get('button').trigger('click')
+
+    expect(wrapper.text()).toContain('1')
+  })
+})

+ 47 - 0
tsconfig.json

@@ -0,0 +1,47 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "module": "ESNext",
+    "target": "es2016",
+    "lib": [
+      "DOM",
+      "ESNext"
+    ],
+    "strict": true,
+    "jsx": "preserve",
+    "esModuleInterop": true,
+    "skipLibCheck": true,
+    "moduleResolution": "bundler",
+    "resolveJsonModule": true,
+    "noUnusedLocals": true,
+    "strictNullChecks": true,
+    "allowJs": true,
+    "forceConsistentCasingInFileNames": true,
+    "types": [
+      "vite/client",
+      "vite-plugin-pages/client",
+      "unplugin-vue-macros/macros-global"
+    ],
+    "paths": {
+      "~/*": [
+        "src/*"
+      ]
+    }
+  },
+  "vueCompilerOptions": {
+    "plugins": [
+      "@vue-macros/volar/define-options",
+      "@vue-macros/volar/define-models",
+      "@vue-macros/volar/define-props",
+      "@vue-macros/volar/define-props-refs",
+      "@vue-macros/volar/short-vmodel",
+      "@vue-macros/volar/define-slots",
+      "@vue-macros/volar/export-props"
+    ]
+  },
+  "exclude": [
+    "dist",
+    "node_modules",
+    "public/config.js"
+  ]
+}

+ 29 - 0
uno.config.ts

@@ -0,0 +1,29 @@
+import {
+  defineConfig,
+  presetAttributify,
+  presetIcons,
+  presetUno,
+  presetWebFonts,
+} from 'unocss'
+
+export default defineConfig({
+  shortcuts: [
+    ['btn', 'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
+    ['icon-btn', 'text-[0.9em] inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600 !outline-none'],
+  ],
+  presets: [
+    presetUno(),
+    presetAttributify(),
+    presetIcons({
+      scale: 1.2,
+      warn: true,
+    }),
+    presetWebFonts({
+      fonts: {
+        sans: 'DM Sans',
+        serif: 'DM Serif Display',
+        mono: 'DM Mono',
+      },
+    }),
+  ],
+})

+ 90 - 0
vite.config.ts

@@ -0,0 +1,90 @@
+/// <reference types="vitest" />
+
+import path from 'node:path'
+import { defineConfig } from 'vite'
+import Vue from '@vitejs/plugin-vue'
+import Pages from 'vite-plugin-pages'
+import Components from 'unplugin-vue-components/vite'
+
+import AutoImport from 'unplugin-auto-import/vite'
+import WindiCSS from 'vite-plugin-windicss'
+import VueMacros from 'unplugin-vue-macros/vite'
+import { VantResolver } from 'unplugin-vue-components/resolvers'
+import legacyPlugin from '@vitejs/plugin-legacy'
+export default defineConfig({
+  base: '',
+  resolve: {
+    alias: {
+      '~/': `${path.resolve(__dirname, 'src')}/`,
+    },
+  },
+  server: {
+    hmr: { overlay: false }, // 禁用或配置 HMR 连接 设置 server.hmr.overlay 为 false 可以禁用服务器错误遮罩层
+  },
+  plugins: [
+    legacyPlugin({
+      targets: ['chrome 52'],  // 需要兼容的目标列表,可以设置多个
+      additionalLegacyPolyfills: ['regenerator-runtime/runtime'] // 面向IE11时需要此插件
+    }),
+
+    VueMacros({
+      defineOptions: false,
+      defineModels: false,
+      plugins: {
+        vue: Vue({
+          script: {
+            propsDestructure: true,
+            defineModel: true,
+          },
+        }),
+      },
+    }),
+
+    // https://github.com/hannoeru/vite-plugin-pages
+    Pages(
+      {
+        dirs: 'src/pages',
+        exclude: ['**/components/*.vue'],
+      },
+    ),
+
+    // https://github.com/antfu/unplugin-auto-import
+    AutoImport({
+      imports: [
+        'vue',
+        'vue/macros',
+        'vue-router',
+        '@vueuse/core',
+      ],
+      dts: true,
+      dirs: [
+        './src/composables',
+        './src/request',
+        './src/utils',
+      ],
+      vueTemplate: true,
+      resolvers: [
+
+      ],
+    }),
+
+    // https://github.com/antfu/vite-plugin-components
+    Components({
+      dirs: ['src/components/'],
+      // allow auto load markdown components under `./src/components/`
+      extensions: ['vue', 'md'],
+      // allow auto import and register components used in markdown
+      include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
+      resolvers: [VantResolver()],
+    }),
+
+    // https://github.com/antfu/unocss
+    // see uno.config.ts for config
+    WindiCSS(),
+  ],
+
+  // https://github.com/vitest-dev/vitest
+  test: {
+    environment: 'jsdom',
+  },
+})