init
22
.editorconfig
Normal file
@ -0,0 +1,22 @@
|
||||
# 告诉EditorConfig插件,这是根文件,不用继续往上查找
|
||||
root = true
|
||||
|
||||
# 匹配全部文件
|
||||
[*]
|
||||
# 设置字符集
|
||||
charset = utf-8
|
||||
# 缩进风格,可选space、tab
|
||||
indent_style = space
|
||||
# 缩进的空格数
|
||||
indent_size = 2
|
||||
# 结尾换行符,可选lf、cr、crlf
|
||||
end_of_line = lf
|
||||
# 在文件结尾插入新行
|
||||
insert_final_newline = true
|
||||
# 删除一行中的前后空格
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# 匹配md结尾的文件
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
14
.env.development
Normal file
@ -0,0 +1,14 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE='uniapp-vue3-project'
|
||||
|
||||
# 开发环境配置
|
||||
VITE_APP_ENV='development'
|
||||
|
||||
# 接口地址
|
||||
VITE_APP_BASE_API='/api'
|
||||
|
||||
# 删除console
|
||||
VITE_DROP_CONSOLE=false
|
||||
|
||||
# 是否开启Mock
|
||||
VITE_USE_MOCK=true
|
11
.env.production
Normal file
@ -0,0 +1,11 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE='uniapp-vue3-project'
|
||||
|
||||
# 生产环境配置
|
||||
VITE_APP_ENV='production'
|
||||
|
||||
# 接口地址
|
||||
VITE_APP_BASE_API='/'
|
||||
|
||||
# 删除console
|
||||
VITE_DROP_CONSOLE=true
|
11
.env.test
Normal file
@ -0,0 +1,11 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE='uniapp-vue3-project'
|
||||
|
||||
# 生产环境配置
|
||||
VITE_APP_ENV='production'
|
||||
|
||||
# 接口地址
|
||||
VITE_APP_BASE_API='/'
|
||||
|
||||
# 删除console
|
||||
VITE_DROP_CONSOLE=true
|
138
.gitignore
vendored
@ -1,132 +1,28 @@
|
||||
# ---> Node
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
*.local
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
#user
|
||||
.hbuilderx
|
||||
unpackage
|
||||
# pnpm-lock.yaml
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
2
.npmrc
Normal file
@ -0,0 +1,2 @@
|
||||
registry=https://registry.npmmirror.com/
|
||||
shamefully-hoist=true
|
10
.prettierrc
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"eslintIntegration": true,
|
||||
"singleQuote": true,
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"printWidth": 120,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "none"
|
||||
}
|
182
README.md
@ -1,2 +1,182 @@
|
||||
# suke-mp
|
||||
### 使用uniapp+vite+vue3+uview-plus3.0 搭建的微信小程序快速开发模版
|
||||
|
||||
使用uniapp+vite+vue3+typescript+uview-plus3.0 搭建的H5和微信小程序快速开发模版
|
||||
|
||||
[uview-plus官方文档](https://uiadmin.net/uview-plus/)
|
||||
|
||||
本项目集众多项目的优点,打造最适合团队协作开发的项目模板。
|
||||
|
||||
在线预览地址:[https://oyjt.github.io/uniapp-vue3-template/](https://oyjt.github.io/uniapp-vue3-template/)
|
||||
|
||||
### 特性
|
||||
|
||||
- [x] 集成uview-plus3.0 ui库
|
||||
- [x] 支持多环境打包构建
|
||||
- [x] 使用pinia状态管理
|
||||
- [x] 封装网络请求,并支持Typescript
|
||||
- [x] 支持路径别名
|
||||
- [x] 支持自动加载组件和API
|
||||
- [x] 自动校验git提交代码格式
|
||||
- [x] 集成ESLint、StyleLint、EditorConfig代码格式规范
|
||||
- [x] Typescript支持
|
||||
- [x] 集成UnoCSS
|
||||
- [x] 集成z-paging下拉刷新功能
|
||||
- [x] 添加页面跳转拦截,登录权限校验
|
||||
- [x] 支持token无痛刷新
|
||||
- [x] 支持持续集成
|
||||
- [x] 项目分包
|
||||
|
||||
### 目录结构
|
||||
项目中采用目前最新的技术方案来实现,目录结构清晰。
|
||||
```
|
||||
uniapp-vue3-project
|
||||
├ build vite插件统一管理
|
||||
│ ├ vite
|
||||
│ └ constant.ts
|
||||
├ scripts 一些脚本
|
||||
│ └ verifyCommit.js
|
||||
├ src
|
||||
│ ├ api 接口管理
|
||||
│ ├ components 公共组件
|
||||
│ ├ hooks 常用hooks封装
|
||||
│ ├ pages 页面管理
|
||||
│ ├ static 静态资源
|
||||
│ ├ store 状态管理
|
||||
│ ├ utils 一些工具
|
||||
│ ├ App.vue
|
||||
│ ├ main.ts
|
||||
│ ├ manifest.json
|
||||
│ ├ pages.json
|
||||
│ ├ permission.ts 页面访问权限控制
|
||||
│ └ uni.scss
|
||||
├ types 全局typescript类型文件
|
||||
│ ├ auto-imports.d.ts
|
||||
│ ├ components.d.ts
|
||||
│ ├ global.d.ts
|
||||
│ └ module.d.ts
|
||||
├ README.md
|
||||
├ eslint.config.js
|
||||
├ index.html
|
||||
├ package.json
|
||||
├ pnpm-lock.yaml
|
||||
├ tsconfig.json
|
||||
├ uno.config.ts
|
||||
└ vite.config.ts
|
||||
```
|
||||
|
||||
#### vite插件管理
|
||||
```
|
||||
build
|
||||
├ vite
|
||||
│ ├ plugins
|
||||
│ │ ├ autoImport.ts 自动导入api
|
||||
│ │ ├ component.ts 自动导入组件
|
||||
│ │ ├ imagemin.ts 图片压缩
|
||||
│ │ ├ index.ts 入口文件
|
||||
│ │ └ unocss.ts unocss插件
|
||||
│ └ proxy.ts 跨域代理配置
|
||||
└ constant.ts 一些常量
|
||||
```
|
||||
|
||||
#### 接口管理
|
||||
```
|
||||
api
|
||||
├ common 通用api
|
||||
│ ├ index.ts
|
||||
│ └ types.ts
|
||||
├ user 用户相关api
|
||||
│ ├ index.ts
|
||||
│ └ types.ts
|
||||
└ index.ts 入口文件
|
||||
```
|
||||
|
||||
#### hooks管理
|
||||
```
|
||||
hooks
|
||||
├ use-clipboard 剪切板
|
||||
│ └ index.ts
|
||||
├ use-loading loading
|
||||
│ └ index.ts
|
||||
├ use-modal 模态框
|
||||
│ └ index.ts
|
||||
├ use-share 分享
|
||||
│ └ index.ts
|
||||
└ index.ts 入口文件
|
||||
```
|
||||
|
||||
### 页面管理
|
||||
```
|
||||
pages
|
||||
├ common 公共页面(分包common)
|
||||
│ ├ login
|
||||
│ │ └ index.vue
|
||||
│ └ webview
|
||||
│ └ index.vue
|
||||
└ tab 主页面(主包)
|
||||
├ home
|
||||
│ └ index.vue
|
||||
├ list
|
||||
│ └ index.vue
|
||||
└ user
|
||||
└ index.vue
|
||||
```
|
||||
|
||||
#### 状态管理
|
||||
```
|
||||
store
|
||||
├ modules
|
||||
│ ├ app app状态
|
||||
│ │ ├ index.ts
|
||||
│ │ └ types.ts
|
||||
│ └ user 用户状态
|
||||
│ ├ index.ts
|
||||
│ └ types.ts
|
||||
└ index.ts 入口文件
|
||||
```
|
||||
|
||||
### 工具方法
|
||||
```
|
||||
utils
|
||||
├ auth token相关方法
|
||||
│ └ index.ts
|
||||
├ common 通用方法
|
||||
│ └ index.ts
|
||||
├ modals 弹窗相关方法
|
||||
│ └ index.ts
|
||||
├ request 网络请求相关方法
|
||||
│ ├ index.ts
|
||||
│ ├ interceptors.ts
|
||||
│ ├ status.ts
|
||||
│ └ type.ts
|
||||
└ index.ts 入口文件
|
||||
```
|
||||
|
||||
### 使用方法
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
|
||||
# 启动H5
|
||||
pnpm dev:h5
|
||||
|
||||
# 启动微信小程序
|
||||
pnpm dev:mp-weixin
|
||||
```
|
||||
|
||||
### 发布
|
||||
|
||||
```bash
|
||||
# 构建测试环境
|
||||
pnpm build:h5
|
||||
pnpm build:mp-weixin
|
||||
|
||||
# 构建生产环境
|
||||
pnpm build:h5-prod
|
||||
pnpm build:mp-weixin-prod
|
||||
```
|
||||
|
||||
### 代码提交
|
||||
```bash
|
||||
pnpm cz
|
||||
```
|
15
build/constant.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @name Config
|
||||
* @description 项目配置
|
||||
*/
|
||||
|
||||
// prefix
|
||||
export const API_PREFIX = '/api';
|
||||
|
||||
// serve
|
||||
export const API_BASE_URL = '/api';
|
||||
export const API_TARGET_URL = 'http://localhost:8080';
|
||||
|
||||
// mock
|
||||
export const MOCK_API_BASE_URL = '/mock/api';
|
||||
export const MOCK_API_TARGET_URL = 'http://localhost:3000';
|
13
build/vite/plugins/autoImport.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @name AutoImportDeps
|
||||
* @description 按需加载,自动引入
|
||||
*/
|
||||
import AutoImport from 'unplugin-auto-import/vite';
|
||||
|
||||
export const AutoImportDeps = () => {
|
||||
return AutoImport({
|
||||
imports: ['vue', 'uni-app', 'pinia'],
|
||||
dts: 'types/auto-imports.d.ts',
|
||||
vueTemplate: true,
|
||||
});
|
||||
};
|
11
build/vite/plugins/component.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name AutoRegistryComponents
|
||||
* @description 按需加载,自动引入
|
||||
*/
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
|
||||
export const AutoRegistryComponents = () => {
|
||||
return Components({
|
||||
dts: 'types/components.d.ts',
|
||||
});
|
||||
};
|
36
build/vite/plugins/imagemin.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @name ConfigImageminPlugin
|
||||
* @description 图片压缩配置
|
||||
*/
|
||||
import viteImagemin from 'vite-plugin-imagemin';
|
||||
|
||||
export const ConfigImageminPlugin = () => {
|
||||
const plugin = viteImagemin({
|
||||
gifsicle: {
|
||||
optimizationLevel: 7,
|
||||
interlaced: false,
|
||||
},
|
||||
mozjpeg: {
|
||||
quality: 20,
|
||||
},
|
||||
optipng: {
|
||||
optimizationLevel: 7,
|
||||
},
|
||||
pngquant: {
|
||||
quality: [0.8, 0.9],
|
||||
speed: 4,
|
||||
},
|
||||
svgo: {
|
||||
plugins: [
|
||||
{
|
||||
name: 'removeViewBox',
|
||||
},
|
||||
{
|
||||
name: 'removeEmptyAttrs',
|
||||
active: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
return plugin;
|
||||
};
|
30
build/vite/plugins/index.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @name createVitePlugins
|
||||
* @description 封装plugins数组统一调用
|
||||
*/
|
||||
import type { PluginOption } from 'vite';
|
||||
import uniPlugin from '@dcloudio/vite-plugin-uni';
|
||||
import { AutoImportDeps } from './autoImport';
|
||||
import { AutoRegistryComponents } from './component';
|
||||
import { ConfigUnoCSSPlugin } from './unocss';
|
||||
import { ConfigImageminPlugin } from './imagemin';
|
||||
|
||||
export default function createVitePlugins(isBuild: boolean) {
|
||||
const vitePlugins: (PluginOption | PluginOption[])[] = [
|
||||
// UnoCSS配置
|
||||
ConfigUnoCSSPlugin(),
|
||||
// 自动按需引入依赖
|
||||
AutoImportDeps(),
|
||||
// 自动按需引入组件(注意:需注册至 uni 之前,否则不会生效)
|
||||
AutoRegistryComponents(),
|
||||
// uni支持
|
||||
uniPlugin(),
|
||||
];
|
||||
|
||||
if (isBuild) {
|
||||
// vite-plugin-imagemin
|
||||
vitePlugins.push(ConfigImageminPlugin());
|
||||
}
|
||||
|
||||
return vitePlugins;
|
||||
}
|
9
build/vite/plugins/unocss.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @name ConfigUnoCSSPlugin
|
||||
* @description UnoCSS相关配置
|
||||
*/
|
||||
import UnoCSS from 'unocss/vite';
|
||||
|
||||
export const ConfigUnoCSSPlugin = () => {
|
||||
return UnoCSS();
|
||||
};
|
21
build/vite/proxy.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import type { ProxyOptions } from 'vite';
|
||||
import { API_BASE_URL, API_TARGET_URL, MOCK_API_BASE_URL, MOCK_API_TARGET_URL } from '../constant';
|
||||
|
||||
type ProxyTargetList = Record<string, ProxyOptions>;
|
||||
|
||||
const init: ProxyTargetList = {
|
||||
// test
|
||||
[API_BASE_URL]: {
|
||||
target: API_TARGET_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(new RegExp(`^${API_BASE_URL}`), ''),
|
||||
},
|
||||
// mock
|
||||
[MOCK_API_BASE_URL]: {
|
||||
target: MOCK_API_TARGET_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(new RegExp(`^${MOCK_API_BASE_URL}`), '/api'),
|
||||
},
|
||||
};
|
||||
|
||||
export default init;
|
58
cz.config.js
Normal file
@ -0,0 +1,58 @@
|
||||
/** @type {import('cz-git').CommitizenGitOptions} */
|
||||
module.exports = {
|
||||
"alias": { "fd": "docs: fix typos" },
|
||||
"messages": {
|
||||
"type": "选择你要提交的类型 :",
|
||||
"scope": "选择一个提交范围(可选):",
|
||||
"customScope": "请输入自定义的提交范围 :",
|
||||
"subject": "填写简短精炼的变更描述 :\n",
|
||||
"body": "填写更加详细的变更描述(可选)。使用 '|' 换行 :\n",
|
||||
"breaking": "列举非兼容性重大的变更(可选)。使用 '|' 换行 :\n",
|
||||
"footerPrefixesSelect": "选择关联issue前缀(可选):",
|
||||
"customFooterPrefix": "输入自定义issue前缀 :",
|
||||
"footer": "列举关联issue (可选) 例如: #31, #I3244 :\n",
|
||||
"confirmCommit": "是否提交或修改commit ?"
|
||||
},
|
||||
"types": [
|
||||
{ "value": "feat", "name": "feat: 新增功能 | A new feature", "emoji": ":sparkles:" },
|
||||
{ "value": "fix", "name": "fix: 修复缺陷 | A bug fix", "emoji": ":bug:" },
|
||||
{ "value": "docs", "name": "docs: 文档更新 | Documentation only changes", "emoji": ":memo:" },
|
||||
{ "value": "style", "name": "style: 代码格式 | Changes that do not affect the meaning of the code", "emoji": ":lipstick:" },
|
||||
{ "value": "refactor", "name": "refactor: 代码重构 | A code change that neither fixes a bug nor adds a feature", "emoji": ":recycle:" },
|
||||
{ "value": "perf", "name": "perf: 性能提升 | A code change that improves performance", "emoji": ":zap:" },
|
||||
{ "value": "test", "name": "test: 测试相关 | Adding missing tests or correcting existing tests", "emoji": ":white_check_mark:" },
|
||||
{ "value": "build", "name": "build: 构建相关 | Changes that affect the build system or external dependencies", "emoji": ":package:" },
|
||||
{ "value": "ci", "name": "ci: 持续集成 | Changes to our CI configuration files and scripts", "emoji": ":ferris_wheel:" },
|
||||
{ "value": "chore", "name": "chore: 其他修改 | Other changes that don't modify src or test files", "emoji": ":hammer:" },
|
||||
{ "value": "revert", "name": "revert: 回退代码 | Reverts a previous commit", "emoji": ":rewind:" }
|
||||
],
|
||||
"useEmoji": false,
|
||||
"emojiAlign": "center",
|
||||
"useAI": false,
|
||||
"aiNumber": 1,
|
||||
"themeColorCode": "",
|
||||
"scopes": [],
|
||||
"allowCustomScopes": true,
|
||||
"allowEmptyScopes": true,
|
||||
"customScopesAlign": "bottom",
|
||||
"customScopesAlias": "custom",
|
||||
"emptyScopesAlias": "empty",
|
||||
"upperCaseSubject": false,
|
||||
"markBreakingChangeMode": false,
|
||||
"allowBreakingChanges": ["feat", "fix"],
|
||||
"breaklineNumber": 100,
|
||||
"breaklineChar": "|",
|
||||
"skipQuestions": [],
|
||||
"issuePrefixes": [{ "value": "closed", "name": "closed: 标记 ISSUES 已完成" }],
|
||||
"customIssuePrefixAlign": "top",
|
||||
"emptyIssuePrefixAlias": "skip",
|
||||
"customIssuePrefixAlias": "custom",
|
||||
"allowCustomIssuePrefix": true,
|
||||
"allowEmptyIssuePrefix": true,
|
||||
"confirmColorize": true,
|
||||
"minSubjectLength": 0,
|
||||
"defaultBody": "",
|
||||
"defaultIssues": "",
|
||||
"defaultScope": "",
|
||||
"defaultSubject": ""
|
||||
}
|
44
deploy.ts
Normal file
@ -0,0 +1,44 @@
|
||||
// @ts-ignore
|
||||
const fs = require( 'fs');
|
||||
// @ts-ignore
|
||||
const ci = require ('miniprogram-ci');
|
||||
// @ts-ignore
|
||||
const path = require ('path');
|
||||
|
||||
// @ts-ignore
|
||||
const envType = process.argv[process.argv.length - 1]; // 获取传入的环境变量名称
|
||||
|
||||
const WX_DESC = envType == 'production' ? '正式环境' : '测试环境' // 备注
|
||||
|
||||
; // @ts-ignore
|
||||
(async () => {
|
||||
// @ts-ignore
|
||||
const manifest = path.resolve(__dirname, './src/manifest.json')
|
||||
// @ts-ignore
|
||||
const manifestConfig = JSON.parse(fs.readFileSync(manifest).toString())
|
||||
const appId = manifestConfig['mp-weixin'].appid
|
||||
const versionName = manifestConfig.versionName || '1.0.0'
|
||||
|
||||
const project = new ci.Project({
|
||||
appid: appId,
|
||||
type: 'miniProgram',
|
||||
// @ts-ignore
|
||||
projectPath: path.join(__dirname, './dist/build/mp-weixin'), // 获取打包的路径
|
||||
// @ts-ignore
|
||||
privateKeyPath: path.join(__dirname, `./keys/private.${appId}.key`), // 你要上传的小程序APPid
|
||||
ignores: ['node_modules/**/*'],
|
||||
})
|
||||
await ci.upload({
|
||||
project,
|
||||
version: versionName,
|
||||
desc: WX_DESC,
|
||||
robot: 1,
|
||||
setting: {
|
||||
es7: true,
|
||||
minifyJS: true, // 压缩 JS 代码
|
||||
minify: true // 压缩所有代码,对应小程序开发者工具的 "压缩代码"
|
||||
},
|
||||
// @ts-ignore
|
||||
onProgressUpdate: console.log,
|
||||
})
|
||||
})()
|
34
eslint.config.js
Normal file
@ -0,0 +1,34 @@
|
||||
const antfu = require('@antfu/eslint-config').default
|
||||
const unocss = require('@unocss/eslint-plugin').default
|
||||
|
||||
module.exports = antfu(
|
||||
{
|
||||
ignores: ['uni_modules'],
|
||||
},
|
||||
{
|
||||
files: ['**/*.vue'],
|
||||
rules: {
|
||||
'vue/block-order': [
|
||||
"error",
|
||||
{
|
||||
"order": ["template", "script", "style"]
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
// 需要尾随逗号
|
||||
// "comma-dangle": ["error", "always"]
|
||||
// 允许console
|
||||
"no-console": "off",
|
||||
// 需要尾随分号
|
||||
"semi": ["error", "always"],
|
||||
// 分号位置
|
||||
"style/semi": "off",
|
||||
// 块内的空行
|
||||
"padded-blocks": ["error", "never"]
|
||||
},
|
||||
},
|
||||
unocss,
|
||||
)
|
20
index.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<title></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
27
keys/private.wx6cbc9f8ba0a7d1c6.key
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAtAuySwDuVUqiKAgtB8yhmDXMbFtgSZQiC4FtahrqAxcwcett
|
||||
khfncnldZZGMUtK3dM/xA5YaKe18vish3qbymwc+863r2NKIFtWhWIglXDuutGgT
|
||||
U9/IqCnh4Dx8Owlw5/EzbvHSXj1/uzwuAVO6cxkkEvz9mxYixuB3YCd8yKQ5rLnu
|
||||
EwNf063fI+jDVUBeyEejSZJ0pi3OzWdXJOjS9UYv2bAYOfJluGucfDXMbfdz8euy
|
||||
T8WRCPJp4X2QoQU5WcSxWnvRSllwAfposZTIcu/msXm77QwoYqCHvJ2suGzL+SXa
|
||||
c73wQB289dXqHbYmSa7bPmTmOEyCPYao0oIh8wIDAQABAoIBACfpb9ywmCTQJqI5
|
||||
LcJM2oWjZ22d0/p7nBBw4JBk5vtIaLTYO7HnGqeFv+6EFVdG3uRGNGdXHvWsb7q4
|
||||
VQnGeIFlWc8q/t1RzllBgkLd62pG0LOtXpt5QEIHBstunRt5PE4uafhuIvRBLc+T
|
||||
x58DsJFTQWv0KUP35D2yBMSL/F19qqF6oe43kYbAei8w608XXKOT/rODdk1Ie3yx
|
||||
AOgM4DvTqcSdqL/SDJ6r8xoplDNk4l+QYCJ9z7JodnJfU5OQVWw8WFWRg3jP9k/S
|
||||
DsojcusTs1s14X8V3/TbR1vVW6g4Q5QnNFozHaKRZAD9DuBStrL02hgAq7oXdg+g
|
||||
IoMR+AECgYEA7VVuOWwt2aWXks9aPpOVs9knMdrpxbO1nE/B2JECICElFtL8xwUi
|
||||
BxWcM++wJNtKTAjJfpnFGZkyHfmnB8V0itGIz1lCZTs/yraJqaLxQdbHjEnCoCRq
|
||||
tl1751hsBS15XFDskKHGFZj0wv9nq3I0wUoMjhsRV+Lopq+uHCodcgMCgYEAwjTO
|
||||
dwh/0cTjiGOV8iX9WdM0I3wnYfcAgDbxGKNyZNw8oQ52vzTn1VG+LcxMcy+OUq/a
|
||||
B/XBjkUI4WyHfK7zM6rhlQB8pWR3i+yPyLXJ69oRQwYAeHQzgZ6Z+sSsusHcG/wN
|
||||
J/Z9EqiWQK/tZsBXQxDpHqxwz9Xl1BlL0xX9BVECgYBHKsr2pR3KmgEtoMfq486M
|
||||
M52xMXfQNOdMjA4QpssAX3ADvBjYhQ2DGlPQrxsesjNBQZFKSUn1Nx70JhyUE/2y
|
||||
csqXgqiKOo4Sd1IocBfwKjuEMcoOw1zMepPg937MvqoZqJqHdDs11rvujS/FFWYE
|
||||
X/QL2MoGlKA2+482GtrhiQKBgQCTt8jnn45hx2nuXxk5w42umkiJSTFHgbJe0+uU
|
||||
+xXTA/YV50OJcrt4daG7gi8QWjbeTCYCcfrUtUvo8z0nKIeSYEMPq/wjbYTE6J4B
|
||||
Y8z/2bHRkiofdPuMd0/V/20G7Nf4bUKwh/tgit0mvOpNgrWdLKq1CyMP4znal5cm
|
||||
Kw520QKBgB+I8yfp/E01ruSccdgRH3WMGStuHhSxcQ8WWD9oFdOn+klxr8h4al3+
|
||||
Ibo6SJkKmwg2/IEsuc52qZPH5594XfQ7HxfVf9RVwdR96epRqah/cHofIVCX1wQB
|
||||
CCirZNNiBe2hQxDyAHn492a6dzcjMojDN5JS+Yt071CB0Tph7+2c
|
||||
-----END RSA PRIVATE KEY-----
|
76
package.json
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "uniapp-vue3-project",
|
||||
"version": "1.0.2",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "uni -p mp-weixin",
|
||||
"build:test": "uni build --mode test -p mp-weixin",
|
||||
"build:prod": "uni build --mode production -p mp-weixin",
|
||||
"deploy:test": "uni build --mode test -p mp-weixin && node deploy.ts test",
|
||||
"deploy:prod": "uni build --mode production -p mp-weixin && node deploy.ts production",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"eslint": "eslint --fix",
|
||||
"stylelint": "stylelint \"src/**/*.(vue|scss|css)\" --fix",
|
||||
"cz": "git add . && npx czg",
|
||||
"postinstall": "simple-git-hooks"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "3.0.0-3090920231225001",
|
||||
"@dcloudio/uni-app-plus": "3.0.0-3090920231225001",
|
||||
"@dcloudio/uni-components": "3.0.0-3090920231225001",
|
||||
"@dcloudio/uni-h5": "3.0.0-3090920231225001",
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-3090920231225001",
|
||||
"@dcloudio/uni-mp-xhs": "3.0.0-3090920231225001",
|
||||
"pinia": "2.0.36",
|
||||
"uview-plus": "^3.1.38",
|
||||
"vue": "3.2.47",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"z-paging": "^2.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "1.1.0",
|
||||
"@dcloudio/types": "^3.4.3",
|
||||
"@dcloudio/uni-automator": "3.0.0-3090920231225001",
|
||||
"@dcloudio/uni-cli-shared": "3.0.0-3090920231225001",
|
||||
"@dcloudio/uni-stacktracey": "3.0.0-3090920231225001",
|
||||
"@dcloudio/vite-plugin-uni": "3.0.0-3090920231225001",
|
||||
"@types/node": "^20.8.10",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"@uni-helper/uni-app-types": "^0.5.9",
|
||||
"@unocss/eslint-plugin": "^0.57.2",
|
||||
"@vue/runtime-core": "^3.2.45",
|
||||
"@vue/tsconfig": "^0.4.0",
|
||||
"czg": "^1.7.1",
|
||||
"eslint": "^8.53.0",
|
||||
"lint-staged": "^15.0.2",
|
||||
"miniprogram-ci": "^1.9.8",
|
||||
"picocolors": "^1.0.0",
|
||||
"postcss-html": "^1.5.0",
|
||||
"postcss-scss": "^4.0.9",
|
||||
"sass": "^1.69.5",
|
||||
"simple-git-hooks": "^2.9.0",
|
||||
"stylelint": "^15.11.0",
|
||||
"stylelint-config-recess-order": "^4.3.0",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-config-standard-vue": "^1.0.0",
|
||||
"stylelint-order": "^6.0.3",
|
||||
"typescript": "^5.2.2",
|
||||
"unocss": "^0.57.2",
|
||||
"unocss-applet": "^0.7.7",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"vite": "^4.5.2",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vue-tsc": "^1.8.22"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,jsx,ts,tsx}": "eslint --fix",
|
||||
"*.{scss,less,style,html}": "stylelint --fix",
|
||||
"*.vue": [
|
||||
"stylelint --fix"
|
||||
]
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"commit-msg": "node scripts/verifyCommit.js"
|
||||
}
|
||||
}
|
14956
pnpm-lock.yaml
Normal file
19
scripts/verifyCommit.js
Normal file
@ -0,0 +1,19 @@
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const pc = require('picocolors');
|
||||
|
||||
const msgPath = path.resolve('.git/COMMIT_EDITMSG');
|
||||
const msg = fs.readFileSync(msgPath, 'utf-8').trim();
|
||||
|
||||
const commitRE =
|
||||
/^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|release)(\(.+\))?: .{1,50}/;
|
||||
|
||||
if(!commitRE.test(msg)) {
|
||||
console.log();
|
||||
console.error(
|
||||
` ${pc.bgRed(pc.white(' ERROR '))} ${pc.red('Git提交信息不符合规范!')}\n\n${pc.green(
|
||||
'推荐使用命令 npm run cz 生成符合规范的Git提交信息'
|
||||
)}\n`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
22
src/App.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { mpUpdate } from '@/utils';
|
||||
|
||||
onLaunch(() => {
|
||||
console.log('App Launch');
|
||||
// #ifdef MP
|
||||
mpUpdate();
|
||||
// #endif
|
||||
});
|
||||
onShow(() => {
|
||||
console.log('App Show');
|
||||
});
|
||||
onHide(() => {
|
||||
console.log('App Hide');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/* 每个页面公共css */
|
||||
@import 'uview-plus/index.scss';
|
||||
@import '@/static/styles/common.scss';
|
||||
</style>
|
17
src/api/common/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 通用接口
|
||||
*/
|
||||
import type { SendCodeParams, SendCodeResult, UploadImageResult } from './types';
|
||||
import { post, upload } from '@/utils/request';
|
||||
|
||||
enum URL {
|
||||
upload = '/common/upload',
|
||||
sendCode = '/sendCode',
|
||||
}
|
||||
|
||||
// 图片上传
|
||||
export const uploadImage = (imagePath: string) =>
|
||||
upload<UploadImageResult>({ url: URL.upload, filePath: imagePath, name: 'file' });
|
||||
|
||||
// 发送验证码
|
||||
export const sendCode = (data: SendCodeParams) => post<SendCodeResult>({ url: URL.sendCode, data });
|
13
src/api/common/types.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export interface UploadImageResult {
|
||||
file: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface SendCodeParams {
|
||||
phone: number;
|
||||
code: number;
|
||||
}
|
||||
|
||||
export interface SendCodeResult {
|
||||
code: number;
|
||||
}
|
4
src/api/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import * as CommonApi from './common';
|
||||
import * as UserApi from './user';
|
||||
|
||||
export { CommonApi, UserApi };
|
18
src/api/user/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 用户信息相关接口
|
||||
*/
|
||||
import type { LoginByCodeParams, LoginParams, LoginResult } from './types';
|
||||
import { get, post } from '@/utils/request';
|
||||
import type { UserState } from '@/store/modules/user/types';
|
||||
|
||||
enum URL {
|
||||
login = '/user/login',
|
||||
loginByCode = '/user/loginByCode',
|
||||
logout = '/user/logout',
|
||||
profile = '/user/profile',
|
||||
}
|
||||
|
||||
export const getUserProfile = () => get<UserState>({ url: URL.profile });
|
||||
export const login = (data: LoginParams) => post<LoginResult>({ url: URL.login, data });
|
||||
export const loginByCode = (data: LoginByCodeParams) => post<any>({ url: URL.loginByCode, data });
|
||||
export const logout = () => post<any>({ url: URL.logout });
|
15
src/api/user/types.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export interface LoginParams {
|
||||
phone: string;
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface LoginByCodeParams {
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface LoginResult {
|
||||
token: string;
|
||||
user_id: number;
|
||||
user_name: string;
|
||||
avatar: string;
|
||||
}
|
103
src/components/page-nav/page-nav.vue
Normal file
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<view class="nav-wrap">
|
||||
<view class="nav-title">
|
||||
<u--image :show-loading="true" src="./static/logo.png" width="70px" height="70px" />
|
||||
<view class="nav-info">
|
||||
<view class="nav-info__title">
|
||||
<text class="nav-info__title__text">uview-plus3</text>
|
||||
</view>
|
||||
<text class="nav-slogan">多平台快速开发的UI框架</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="nav-desc">{{ desc }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PageNav',
|
||||
props: {
|
||||
desc: String,
|
||||
title: String,
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nav-wrap {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.lang {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.nav-info {
|
||||
margin-left: 15px;
|
||||
|
||||
&__title {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
&__text {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
font-size: 25px;
|
||||
text-align: left;
|
||||
|
||||
/* #endif */
|
||||
color: $u-main-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__jump {
|
||||
margin-left: 20px;
|
||||
font-size: 12px;
|
||||
color: $u-primary;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
height: auto;
|
||||
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.nav-slogan {
|
||||
font-size: 14px;
|
||||
color: $u-tips-color;
|
||||
}
|
||||
|
||||
.nav-desc {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
color: $u-content-color;
|
||||
line-height: 20px;
|
||||
}
|
||||
</style>
|
6
src/hooks/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import useShare from './use-share';
|
||||
import useLoading from './use-loading';
|
||||
import useModal from './use-modal';
|
||||
import useClipboard from './use-clipboard';
|
||||
|
||||
export { useShare, useLoading, useModal, useClipboard };
|
33
src/hooks/use-clipboard/index.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 剪切板
|
||||
*/
|
||||
|
||||
interface SetClipboardDataOptions {
|
||||
data: string;
|
||||
showToast?: boolean;
|
||||
}
|
||||
|
||||
export default function useClipboard() {
|
||||
const setClipboardData = ({ data, showToast = true }: SetClipboardDataOptions) => {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
uni.setClipboardData({
|
||||
data,
|
||||
showToast,
|
||||
success: ({ data }) => resolve(data),
|
||||
fail: error => reject(error),
|
||||
});
|
||||
});
|
||||
};
|
||||
const getClipboardData = () => {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
uni.getClipboardData({
|
||||
success: ({ data }) => resolve(data),
|
||||
fail: error => reject(error),
|
||||
});
|
||||
});
|
||||
};
|
||||
return {
|
||||
setClipboardData,
|
||||
getClipboardData,
|
||||
};
|
||||
}
|
19
src/hooks/use-loading/index.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* loading 提示框
|
||||
*/
|
||||
|
||||
export default function useLoading() {
|
||||
const showLoading = (content = '加载中') => {
|
||||
uni.showLoading({
|
||||
title: content,
|
||||
mask: true,
|
||||
});
|
||||
};
|
||||
const hideLoading = () => {
|
||||
uni.hideLoading();
|
||||
};
|
||||
return {
|
||||
showLoading,
|
||||
hideLoading,
|
||||
};
|
||||
}
|
21
src/hooks/use-modal/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Dialog 提示框
|
||||
*/
|
||||
export default function useModal() {
|
||||
const showModal = (content: string, options: UniApp.ShowModalOptions) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.showModal({
|
||||
title: '温馨提示',
|
||||
content,
|
||||
showCancel: false,
|
||||
confirmColor: '#1677FF',
|
||||
success: res => resolve(res),
|
||||
fail: () => reject(new Error('Alert 调用失败 !')),
|
||||
...options,
|
||||
})
|
||||
})
|
||||
}
|
||||
return {
|
||||
showModal,
|
||||
}
|
||||
}
|
32
src/hooks/use-share/index.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 小程序分享
|
||||
*/
|
||||
interface UseShareOptions {
|
||||
title?: string;
|
||||
path?: string;
|
||||
query?: string;
|
||||
imageUrl?: string;
|
||||
}
|
||||
|
||||
export default function useShare(options?: UseShareOptions) {
|
||||
// #ifdef MP-WEIXIN
|
||||
const title = options?.title ?? '';
|
||||
const path = options?.path ?? '';
|
||||
const query = options?.query ?? '';
|
||||
const imageUrl = options?.imageUrl ?? '';
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
title,
|
||||
path: path ? `${path}${query ? `?${query}` : ''}` : '',
|
||||
imageUrl,
|
||||
};
|
||||
});
|
||||
onShareTimeline(() => {
|
||||
return {
|
||||
title,
|
||||
query: options?.query ?? '',
|
||||
imageUrl,
|
||||
};
|
||||
});
|
||||
// #endif
|
||||
}
|
32
src/main.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { createSSRApp } from 'vue';
|
||||
|
||||
// 引入UnoCSS
|
||||
import 'uno.css';
|
||||
|
||||
// 引入uview-plus
|
||||
import uviewPlus from 'uview-plus';
|
||||
import App from '@/App.vue';
|
||||
|
||||
// 引入状态管理
|
||||
import setupStore from '@/store';
|
||||
|
||||
// 引入请求封装
|
||||
import setupRequest from '@/utils/request';
|
||||
|
||||
// 权限管理
|
||||
import '@/permission';
|
||||
|
||||
// #ifdef VUE3
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App);
|
||||
app.use(uviewPlus);
|
||||
// 状态管理
|
||||
setupStore(app);
|
||||
// 网络请求
|
||||
setupRequest();
|
||||
|
||||
return {
|
||||
app,
|
||||
};
|
||||
}
|
||||
// #endif
|
47
src/manifest.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "SUKE-MP",
|
||||
"appid": "",
|
||||
"description": "",
|
||||
"versionName": "1.0.0",
|
||||
"versionCode": "100",
|
||||
"transformPx": false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus":
|
||||
{
|
||||
"usingComponents": true,
|
||||
"nvueStyleCompiler": "uni-app",
|
||||
"compilerVersion": 3,
|
||||
"splashscreen":
|
||||
{
|
||||
"alwaysShowBeforeRender": true,
|
||||
"waiting": true,
|
||||
"autoclose": true,
|
||||
"delay": 0
|
||||
},
|
||||
/* 模块配置 */
|
||||
"modules": {}
|
||||
},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin":
|
||||
{
|
||||
"appid": "wx67a750d0ceed4d88",
|
||||
"setting":
|
||||
{
|
||||
"urlCheck": false
|
||||
},
|
||||
"usingComponents": true
|
||||
},
|
||||
"uniStatistics":
|
||||
{
|
||||
"enable": false
|
||||
},
|
||||
"vueVersion": "3",
|
||||
"h5":
|
||||
{
|
||||
"router":
|
||||
{
|
||||
"mode": "hash",
|
||||
"base": "/uniapp-vue3-template/"
|
||||
}
|
||||
}
|
||||
}
|
97
src/pages.json
Normal file
@ -0,0 +1,97 @@
|
||||
{
|
||||
"easycom": {
|
||||
"custom": {
|
||||
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
||||
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
||||
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue",
|
||||
"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
|
||||
}
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/home/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/qrcode/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "支付码"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mall/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [
|
||||
{
|
||||
"root": "pages/common",
|
||||
"pages": [
|
||||
{
|
||||
"path": "login/index",
|
||||
"navigationStyle": "custom"
|
||||
},
|
||||
{
|
||||
"path": "webview/index",
|
||||
"navigationBarTitleText": "网页"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"preloadRule": {
|
||||
"pages/home/index": {
|
||||
"network": "all",
|
||||
"packages": [
|
||||
"pages/common"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#ABABAB",
|
||||
"selectedColor": "#333333",
|
||||
"borderStyle": "black",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/home/index",
|
||||
"iconPath": "static/images/ic_tab_home_normal.png",
|
||||
"selectedIconPath": "static/images/ic_tab_home_active.png",
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/qrcode/index",
|
||||
"iconPath": "static/images/ic_tab_qrcode_normal.png",
|
||||
"selectedIconPath": "static/images/ic_tab_qrcode_active.png",
|
||||
"text": "支付码"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/mall/index",
|
||||
"iconPath": "static/images/ic_tab_mall_normal.png",
|
||||
"selectedIconPath": "static/images/ic_tab_mall_active.png",
|
||||
"text": "商城"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/mine/index",
|
||||
"iconPath": "static/images/ic_tab_mine_normal.png",
|
||||
"selectedIconPath": "static/images/ic_tab_mine_active.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "uni-app",
|
||||
"navigationBarBackgroundColor": "#F8F8F8",
|
||||
"backgroundColor": "#F8F8F8"
|
||||
}
|
||||
}
|
167
src/pages/common/login/index.vue
Normal file
@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="login-form-wrap">
|
||||
<view class="title">
|
||||
欢迎登录
|
||||
</view>
|
||||
<input v-model="tel" class="u-border-bottom" type="number" placeholder="请输入手机号">
|
||||
<view class="u-border-bottom my-40rpx flex">
|
||||
<input v-model="code" class="flex-1" type="number" placeholder="请输入验证码">
|
||||
<view>
|
||||
<u-code ref="uCodeRef" @change="codeChange" />
|
||||
<u-button :text="tips" type="success" size="mini" @click="getCode" />
|
||||
</view>
|
||||
</view>
|
||||
<button :style="[inputStyle]" class="login-btn" @tap="submit">
|
||||
登录
|
||||
</button>
|
||||
|
||||
<view class="alternative">
|
||||
<view class="password">
|
||||
密码登录
|
||||
</view>
|
||||
<view class="issue">
|
||||
遇到问题
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="login-type-wrap">
|
||||
<view class="item wechat">
|
||||
<view class="icon">
|
||||
<u-icon size="35" name="weixin-fill" color="rgb(83,194,64)" />
|
||||
</view>
|
||||
微信
|
||||
</view>
|
||||
<view class="item QQ">
|
||||
<view class="icon">
|
||||
<u-icon size="35" name="qq-fill" color="rgb(17,183,233)" />
|
||||
</view>
|
||||
QQ
|
||||
</view>
|
||||
</view>
|
||||
<view class="hint">
|
||||
登录代表同意
|
||||
<text class="link">
|
||||
用户协议、隐私政策,
|
||||
</text>
|
||||
并授权使用您的账号信息(如昵称、头像、收获地址)以便您统一管理
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import uCode from 'uview-plus/components/u-code/u-code.vue';
|
||||
import { setToken } from '@/utils/auth';
|
||||
|
||||
const tel = ref<string>('18502811111');
|
||||
const code = ref<string>('1234');
|
||||
const tips = ref<string>();
|
||||
const uCodeRef = ref<InstanceType<typeof uCode> | null>(null);
|
||||
|
||||
const inputStyle = computed<CSSStyleDeclaration>(() => {
|
||||
const style = {} as CSSStyleDeclaration;
|
||||
if (tel.value && code.value) {
|
||||
style.color = '#fff';
|
||||
style.backgroundColor = uni.$u.color.warning;
|
||||
}
|
||||
return style;
|
||||
});
|
||||
|
||||
function codeChange(text: string) {
|
||||
tips.value = text;
|
||||
}
|
||||
|
||||
function getCode() {
|
||||
if (uCodeRef.value?.canGetCode) {
|
||||
// 模拟向后端请求验证码
|
||||
uni.showLoading({
|
||||
title: '正在获取验证码',
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
uni.$u.toast('验证码已发送');
|
||||
// 通知验证码组件内部开始倒计时
|
||||
uCodeRef.value?.start();
|
||||
}, 1000);
|
||||
}
|
||||
else {
|
||||
uni.$u.toast('倒计时结束后再发送');
|
||||
}
|
||||
}
|
||||
|
||||
function submit() {
|
||||
if (uni.$u.test.mobile(tel.value)) {
|
||||
setToken('1234567890');
|
||||
uni.reLaunch({ url: '/pages/home/index' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-form-wrap {
|
||||
margin: 80rpx auto 0;
|
||||
width: 600rpx;
|
||||
|
||||
.title {
|
||||
margin-bottom: 100rpx;
|
||||
font-size: 60rpx;
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
input {
|
||||
padding-bottom: 6rpx;
|
||||
margin-bottom: 10rpx;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin-top: 8rpx;
|
||||
margin-bottom: 60rpx;
|
||||
color: $u-info;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
padding: 12rpx 0;
|
||||
font-size: 30rpx;
|
||||
color: $u-tips-color;
|
||||
background-color: rgb(253 243 208);
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.alternative {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 30rpx;
|
||||
color: $u-tips-color;
|
||||
}
|
||||
}
|
||||
|
||||
.login-type-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 350rpx 150rpx 150rpx;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
color: $u-content-color;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.hint {
|
||||
padding: 20rpx 40rpx;
|
||||
font-size: 20rpx;
|
||||
color: $u-tips-color;
|
||||
|
||||
.link {
|
||||
color: $u-warning;
|
||||
}
|
||||
}
|
||||
</style>
|
12
src/pages/common/webview/index.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<web-view class="h-full" :src="url" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const url = ref<string>('');
|
||||
|
||||
onLoad((params: any) => {
|
||||
if (params.url)
|
||||
url.value = params.url;
|
||||
});
|
||||
</script>
|
18
src/pages/home/index.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<view class='flex flex-col items-center justify-center'>
|
||||
<swiper class='swiper'>
|
||||
<swiper-item v-for='(item, index) in bannerList' :key='index'>
|
||||
<image :src='item' mode='aspectFill' />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
const store = useUserStore();
|
||||
|
||||
const bannerList = ref<string[]>(['1', '2', '3', '4']);
|
||||
console.log('store.user_name', store.user_name);
|
||||
</script>
|
25
src/pages/mall/index.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<image
|
||||
class="mb-50rpx mt-200rpx h-200rpx w-200rpx"
|
||||
src="@/static/images/logo.png"
|
||||
width="200rpx"
|
||||
height="200rpx"
|
||||
/>
|
||||
<view class="flex justify-center">
|
||||
<text class="font-size-36rpx color-gray-700">
|
||||
{{ title }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
const title = ref<string>();
|
||||
title.value = import.meta.env.VITE_APP_TITLE;
|
||||
|
||||
const store = useUserStore();
|
||||
console.log('store.user_name', store.user_name);
|
||||
</script>
|
25
src/pages/mine/index.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<image
|
||||
class="mb-50rpx mt-200rpx h-200rpx w-200rpx"
|
||||
src="@/static/images/logo.png"
|
||||
width="200rpx"
|
||||
height="200rpx"
|
||||
/>
|
||||
<view class="flex justify-center">
|
||||
<text class="font-size-36rpx color-gray-700">
|
||||
{{ title }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
const title = ref<string>();
|
||||
title.value = import.meta.env.VITE_APP_TITLE;
|
||||
|
||||
const store = useUserStore();
|
||||
console.log('store.user_name', store.user_name);
|
||||
</script>
|
25
src/pages/qrcode/index.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<image
|
||||
class="mb-50rpx mt-200rpx h-200rpx w-200rpx"
|
||||
src="@/static/images/logo.png"
|
||||
width="200rpx"
|
||||
height="200rpx"
|
||||
/>
|
||||
<view class="flex justify-center">
|
||||
<text class="font-size-36rpx color-gray-700">
|
||||
{{ title }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
const title = ref<string>();
|
||||
title.value = import.meta.env.VITE_APP_TITLE;
|
||||
|
||||
const store = useUserStore();
|
||||
console.log('store.user_name', store.user_name);
|
||||
</script>
|
37
src/permission.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { getToken } from '@/utils/auth';
|
||||
|
||||
// 登录页面
|
||||
const loginPage = '/pages/common/login/index';
|
||||
// 页面白名单
|
||||
const whiteList = ['/', '/pages/common/login/index', '/pages/home/index'];
|
||||
|
||||
// 检查地址白名单
|
||||
function checkWhite(url: string) {
|
||||
const path = url.split('?')[0];
|
||||
return whiteList.includes(path);
|
||||
}
|
||||
|
||||
// 页面跳转验证拦截器
|
||||
const list = ['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'];
|
||||
list.forEach((item) => {
|
||||
uni.addInterceptor(item, {
|
||||
invoke(to) {
|
||||
if (getToken()) {
|
||||
if (to.url === loginPage)
|
||||
uni.reLaunch({ url: '/' });
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (checkWhite(to.url))
|
||||
return true;
|
||||
|
||||
uni.reLaunch({ url: loginPage });
|
||||
return false;
|
||||
}
|
||||
},
|
||||
fail(err) {
|
||||
console.log(err);
|
||||
},
|
||||
});
|
||||
});
|
BIN
src/static/images/ic_tab_home_active.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
src/static/images/ic_tab_home_normal.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
src/static/images/ic_tab_mall_active.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
src/static/images/ic_tab_mall_normal.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/static/images/ic_tab_mine_active.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
src/static/images/ic_tab_mine_normal.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
src/static/images/ic_tab_qrcode_active.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/static/images/ic_tab_qrcode_normal.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/static/images/logo.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
4
src/static/styles/common.scss
Normal file
@ -0,0 +1,4 @@
|
||||
page {
|
||||
font-size: 28rpx;
|
||||
background-color: #f9f9f8;
|
||||
}
|
20
src/store/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import type { App } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
// 导入子模块
|
||||
import useAppStore from './modules/app';
|
||||
import useUserStore from './modules/user';
|
||||
|
||||
// import piniaPersist from 'pinia-plugin-persist-uni';
|
||||
|
||||
// 安装pinia状态管理插件
|
||||
function setupStore(app: App) {
|
||||
const store = createPinia();
|
||||
// store.use(piniaPersist);
|
||||
|
||||
app.use(store);
|
||||
}
|
||||
|
||||
// 导出模块
|
||||
export { useAppStore, useUserStore };
|
||||
export default setupStore;
|
58
src/store/modules/app/index.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import type { AppState } from './types';
|
||||
|
||||
const useAppStore = defineStore('app', {
|
||||
state: (): AppState => ({
|
||||
systemInfo: {} as UniApp.GetSystemInfoResult,
|
||||
}),
|
||||
getters: {
|
||||
getSystemInfo(): UniApp.GetSystemInfoResult {
|
||||
return this.systemInfo;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setSystemInfo(info: UniApp.GetSystemInfoResult) {
|
||||
this.systemInfo = info;
|
||||
},
|
||||
initSystemInfo() {
|
||||
uni.getSystemInfo({
|
||||
success: (res: UniApp.GetSystemInfoResult) => {
|
||||
this.setSystemInfo(res);
|
||||
},
|
||||
fail: (err: any) => {
|
||||
console.error(err);
|
||||
},
|
||||
});
|
||||
},
|
||||
checkUpdate() {
|
||||
const updateManager = uni.getUpdateManager();
|
||||
updateManager.onCheckForUpdate((res: UniApp.OnCheckForUpdateResult) => {
|
||||
// 请求完新版本信息的回调
|
||||
|
||||
console.log(res.hasUpdate);
|
||||
});
|
||||
updateManager.onUpdateReady(() => {
|
||||
uni.showModal({
|
||||
title: '更新提示',
|
||||
content: '新版本已经准备好,是否重启应用?',
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
|
||||
updateManager.applyUpdate();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
updateManager.onUpdateFailed((res: any) => {
|
||||
console.error(res);
|
||||
// 新的版本下载失败
|
||||
uni.showToast({
|
||||
title: '更新失败',
|
||||
icon: 'error',
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default useAppStore;
|
3
src/store/modules/app/types.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface AppState {
|
||||
systemInfo: UniApp.GetSystemInfoResult;
|
||||
}
|
82
src/store/modules/user/index.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import type { UserState, providerType } from './types';
|
||||
import {
|
||||
getUserProfile,
|
||||
loginByCode,
|
||||
login as userLogin,
|
||||
logout as userLogout,
|
||||
} from '@/api/user/index';
|
||||
import { clearToken, setToken } from '@/utils/auth';
|
||||
import type { LoginParams } from '@/api/user/types';
|
||||
|
||||
const useUserStore = defineStore('user', {
|
||||
state: (): UserState => ({
|
||||
user_id: '',
|
||||
user_name: '江阳小道',
|
||||
avatar: '',
|
||||
token: '',
|
||||
}),
|
||||
getters: {
|
||||
userInfo(state: UserState): UserState {
|
||||
return { ...state };
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// 设置用户的信息
|
||||
setInfo(partial: Partial<UserState>) {
|
||||
this.$patch(partial);
|
||||
},
|
||||
// 重置用户信息
|
||||
resetInfo() {
|
||||
this.$reset();
|
||||
},
|
||||
// 获取用户信息
|
||||
async info() {
|
||||
const result = await getUserProfile();
|
||||
this.setInfo(result);
|
||||
},
|
||||
// 异步登录并存储token
|
||||
login(loginForm: LoginParams) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const result = await userLogin(loginForm);
|
||||
const token = result?.token;
|
||||
if (token) {
|
||||
setToken(token);
|
||||
}
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
});
|
||||
},
|
||||
// Logout
|
||||
async logout() {
|
||||
await userLogout();
|
||||
this.resetInfo();
|
||||
clearToken();
|
||||
},
|
||||
// 小程序授权登录
|
||||
authLogin(provider: providerType = 'weixin') {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.login({
|
||||
provider,
|
||||
success: async (result: UniApp.LoginRes) => {
|
||||
if (result.code) {
|
||||
const res = await loginByCode({ code: result.code });
|
||||
resolve(res);
|
||||
} else {
|
||||
reject(new Error(result.errMsg));
|
||||
}
|
||||
},
|
||||
fail: (err: any) => {
|
||||
console.error(`login error: ${err}`);
|
||||
reject(err);
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default useUserStore;
|
16
src/store/modules/user/types.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export type RoleType = '' | '*' | 'user';
|
||||
export interface UserState {
|
||||
user_id?: string;
|
||||
user_name?: string;
|
||||
avatar?: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
export type providerType =
|
||||
| 'weixin'
|
||||
| 'qq'
|
||||
| 'sinaweibo'
|
||||
| 'xiaomi'
|
||||
| 'apple'
|
||||
| 'univerify'
|
||||
| undefined;
|
1
src/uni.scss
Normal file
@ -0,0 +1 @@
|
||||
@import 'uview-plus/theme.scss';
|
15
src/utils/auth/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
const TokenKey = 'admin-token';
|
||||
const TokenPrefix = 'Bearer ';
|
||||
function isLogin() {
|
||||
return !!uni.getStorageSync(TokenKey);
|
||||
}
|
||||
function getToken() {
|
||||
return uni.getStorageSync(TokenKey);
|
||||
}
|
||||
function setToken(token: string) {
|
||||
uni.setStorageSync(TokenKey, token);
|
||||
}
|
||||
function clearToken() {
|
||||
uni.removeStorageSync(TokenKey);
|
||||
}
|
||||
export { TokenPrefix, isLogin, getToken, setToken, clearToken };
|
28
src/utils/common/index.ts
Normal file
@ -0,0 +1,28 @@
|
||||
// 小程序更新检测
|
||||
export function mpUpdate() {
|
||||
const updateManager = uni.getUpdateManager();
|
||||
updateManager.onCheckForUpdate((res) => {
|
||||
// 请求完新版本信息的回调
|
||||
console.log(res.hasUpdate);
|
||||
});
|
||||
updateManager.onUpdateReady(() => {
|
||||
uni.showModal({
|
||||
title: '更新提示',
|
||||
content: '检测到新版本,是否下载新版本并重启小程序?',
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
|
||||
updateManager.applyUpdate();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
updateManager.onUpdateFailed(() => {
|
||||
// 新的版本下载失败
|
||||
uni.showModal({
|
||||
title: '已经有新版本了哟~',
|
||||
content: '新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~',
|
||||
showCancel: false,
|
||||
});
|
||||
});
|
||||
}
|
4
src/utils/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './auth';
|
||||
export * from './common';
|
||||
export * from './modals';
|
||||
export * from './request';
|
81
src/utils/modals/index.ts
Normal file
@ -0,0 +1,81 @@
|
||||
interface IShowToastOptions {
|
||||
title?: string
|
||||
icon?: 'success' | 'loading' | 'error' | 'none'
|
||||
image?: string
|
||||
duration?: number
|
||||
position?: 'top' | 'center' | 'bottom'
|
||||
mask?: boolean
|
||||
}
|
||||
|
||||
interface ILoadingOptions {
|
||||
show?: (content?: string) => void
|
||||
hide?: () => void
|
||||
}
|
||||
|
||||
interface IShowModalOptions {
|
||||
title?: string
|
||||
content?: string
|
||||
showCancel?: boolean
|
||||
cancelText?: string
|
||||
cancelColor?: string
|
||||
confirmText?: string
|
||||
confirmColor?: string
|
||||
editable?: boolean
|
||||
placeholderText?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 轻提示
|
||||
* @param {string} content 提示内容
|
||||
* @param {object} option 配置
|
||||
*/
|
||||
export function Toast(content: string, option: IShowToastOptions = {}) {
|
||||
uni.showToast({
|
||||
title: content,
|
||||
icon: 'none',
|
||||
mask: true,
|
||||
duration: 1500,
|
||||
...option,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loading 提示框
|
||||
* @param {string} content 提示内容
|
||||
*/
|
||||
export const Loading: ILoadingOptions = {
|
||||
show: (content = '加载中') => {
|
||||
uni.showLoading({
|
||||
title: content,
|
||||
mask: true,
|
||||
});
|
||||
},
|
||||
hide: () => {
|
||||
uni.hideLoading();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Dialog 提示框
|
||||
* @param {string} content 提示内容
|
||||
* @param {object} option 配置
|
||||
*/
|
||||
export function Dialog(content: string, option: IShowModalOptions = {}) {
|
||||
option.showCancel = false;
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.showModal({
|
||||
title: '温馨提示',
|
||||
content,
|
||||
showCancel: false,
|
||||
confirmColor: '#1677FF',
|
||||
success(res) {
|
||||
if (res.confirm)
|
||||
resolve(res);
|
||||
},
|
||||
fail() {
|
||||
reject(new Error('Alert 调用失败 !'));
|
||||
},
|
||||
...option,
|
||||
});
|
||||
});
|
||||
}
|
42
src/utils/request/index.ts
Normal file
@ -0,0 +1,42 @@
|
||||
// 引入配置
|
||||
import type { HttpRequestConfig } from 'uview-plus/libs/luch-request/index';
|
||||
import { requestInterceptors, responseInterceptors } from './interceptors';
|
||||
import type { IResponse } from './type';
|
||||
|
||||
// 引入拦截器配置
|
||||
export function setupRequest() {
|
||||
uni.$u.http.setConfig((defaultConfig: HttpRequestConfig) => {
|
||||
/* defaultConfig 为默认全局配置 */
|
||||
defaultConfig.baseURL = import.meta.env.VITE_APP_BASE_API;
|
||||
return defaultConfig;
|
||||
});
|
||||
requestInterceptors();
|
||||
responseInterceptors();
|
||||
}
|
||||
|
||||
export function request<T = any>(config: HttpRequestConfig): Promise<T> {
|
||||
return new Promise((resolve) => {
|
||||
uni.$u.http.request(config).then((res: IResponse) => {
|
||||
const { result } = res;
|
||||
resolve(result as T);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function get<T = any>(config: HttpRequestConfig): Promise<T> {
|
||||
return request({ ...config, method: 'GET' });
|
||||
}
|
||||
|
||||
export function post<T = any>(config: HttpRequestConfig): Promise<T> {
|
||||
return request({ ...config, method: 'POST' });
|
||||
}
|
||||
|
||||
export function upload<T = any>(config: HttpRequestConfig): Promise<T> {
|
||||
return request({ ...config, method: 'UPLOAD' });
|
||||
}
|
||||
|
||||
export function download<T = any>(config: HttpRequestConfig): Promise<T> {
|
||||
return request({ ...config, method: 'DOWNLOAD' });
|
||||
}
|
||||
|
||||
export default setupRequest;
|
104
src/utils/request/interceptors.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import type {
|
||||
HttpError,
|
||||
HttpRequestConfig,
|
||||
HttpResponse,
|
||||
} from 'uview-plus/libs/luch-request/index';
|
||||
import { showMessage } from './status';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
|
||||
// 是否正在刷新token的标记
|
||||
let isRefreshing: boolean = false;
|
||||
// 重试队列,每一项将是一个待执行的函数形式
|
||||
let requestQueue: (() => void)[] = [];
|
||||
|
||||
function requestInterceptors() {
|
||||
/**
|
||||
* 请求拦截
|
||||
* @param {Object} http
|
||||
*/
|
||||
uni.$u.http.interceptors.request.use(
|
||||
(config: HttpRequestConfig) => {
|
||||
// 可使用async await 做异步操作
|
||||
// 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
|
||||
config.data = config.data || {};
|
||||
// token设置
|
||||
const token = getToken();
|
||||
if (token && config.header) {
|
||||
config.header.token = token;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(
|
||||
config: any, // 可使用async await 做异步操作
|
||||
) => Promise.reject(config),
|
||||
);
|
||||
}
|
||||
function responseInterceptors() {
|
||||
/**
|
||||
* 响应拦截
|
||||
* @param {Object} http
|
||||
*/
|
||||
uni.$u.http.interceptors.response.use(
|
||||
async (response: HttpResponse) => {
|
||||
/* 对响应成功做点什么 可使用async await 做异步操作 */
|
||||
const data = response.data;
|
||||
// 配置参数
|
||||
const config = response.config;
|
||||
// 自定义参数
|
||||
const custom = config?.custom;
|
||||
|
||||
// 请求成功则返回结果
|
||||
if (data.code === 200) {
|
||||
return data || {};
|
||||
}
|
||||
|
||||
// 登录状态失效,重新登录
|
||||
if (data.code === 401) {
|
||||
// 是否在获取token中,防止重复获取
|
||||
if (!isRefreshing) {
|
||||
// 修改登录状态为true
|
||||
isRefreshing = true;
|
||||
await useUserStore().authLogin();
|
||||
// 登录完成之后,开始执行队列请求
|
||||
requestQueue.forEach(cb => cb());
|
||||
// 重试完了清空这个队列
|
||||
requestQueue = [];
|
||||
isRefreshing = false;
|
||||
// 重新执行本次请求
|
||||
return uni.$u.http.request(config);
|
||||
} else {
|
||||
return new Promise(resolve => {
|
||||
// 将resolve放进队列,用一个函数形式来保存,等登录后直接执行
|
||||
requestQueue.push(() => {
|
||||
resolve(uni.$u.http.request(config));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示
|
||||
if (custom?.toast !== false) {
|
||||
uni.$u.toast(data.message);
|
||||
}
|
||||
|
||||
// 如果需要catch返回,则进行reject
|
||||
if (custom?.catch) {
|
||||
return Promise.reject(data);
|
||||
} else {
|
||||
// 否则返回一个pending中的promise
|
||||
return new Promise(() => {});
|
||||
}
|
||||
},
|
||||
(response: HttpError) => {
|
||||
if (response.statusCode) {
|
||||
// 请求已发出,但是不在2xx的范围
|
||||
showMessage(response.statusCode);
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
showMessage('网络连接异常,请稍后再试!');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export { requestInterceptors, responseInterceptors };
|
41
src/utils/request/status.ts
Normal file
@ -0,0 +1,41 @@
|
||||
export const showMessage = (status: number | string): string => {
|
||||
let message = '';
|
||||
switch (status) {
|
||||
case 400:
|
||||
message = '请求错误(400)';
|
||||
break;
|
||||
case 401:
|
||||
message = '未授权,请重新登录(401)';
|
||||
break;
|
||||
case 403:
|
||||
message = '拒绝访问(403)';
|
||||
break;
|
||||
case 404:
|
||||
message = '请求出错(404)';
|
||||
break;
|
||||
case 408:
|
||||
message = '请求超时(408)';
|
||||
break;
|
||||
case 500:
|
||||
message = '服务器错误(500)';
|
||||
break;
|
||||
case 501:
|
||||
message = '服务未实现(501)';
|
||||
break;
|
||||
case 502:
|
||||
message = '网络错误(502)';
|
||||
break;
|
||||
case 503:
|
||||
message = '服务不可用(503)';
|
||||
break;
|
||||
case 504:
|
||||
message = '网络超时(504)';
|
||||
break;
|
||||
case 505:
|
||||
message = 'HTTP版本不受支持(505)';
|
||||
break;
|
||||
default:
|
||||
message = `连接出错(${status})!`;
|
||||
}
|
||||
return `${message},请检查网络或联系管理员!`;
|
||||
};
|
7
src/utils/request/type.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// 返回res.data的interface
|
||||
export interface IResponse<T = any> {
|
||||
code: number | string;
|
||||
result: T;
|
||||
message: string;
|
||||
status: string | number;
|
||||
}
|
116
stylelint.config.js
Normal file
@ -0,0 +1,116 @@
|
||||
module.exports = {
|
||||
"extends": ["stylelint-config-standard", "stylelint-config-standard-vue"],
|
||||
"plugins": ["stylelint-order"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.(scss|css|vue|html)"],
|
||||
"customSyntax": "postcss-scss"
|
||||
},
|
||||
{
|
||||
"files": ["**/*.(html|vue)"],
|
||||
"customSyntax": "postcss-html"
|
||||
}
|
||||
],
|
||||
"ignoreFiles": [
|
||||
"**/*.js",
|
||||
"**/*.jsx",
|
||||
"**/*.tsx",
|
||||
"**/*.ts",
|
||||
"**/*.json",
|
||||
"**/*.md",
|
||||
"**/*.yaml",
|
||||
"dist/*",
|
||||
"uni_modules/*"
|
||||
],
|
||||
"rules": {
|
||||
"import-notation": "string",
|
||||
"unit-no-unknown": [true, { "ignoreUnits": ["rpx"] }],
|
||||
"no-descending-specificity": null,
|
||||
"selector-pseudo-element-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignorePseudoElements": ["v-deep"]
|
||||
}
|
||||
],
|
||||
"selector-pseudo-class-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignorePseudoClasses": ["deep"]
|
||||
}
|
||||
],
|
||||
"selector-type-no-unknown": [true, { "ignoreTypes": ["page", "radio", "checkbox", "scroll-view"] }],
|
||||
"order/properties-order": [
|
||||
"position",
|
||||
"top",
|
||||
"right",
|
||||
"bottom",
|
||||
"left",
|
||||
"z-index",
|
||||
"display",
|
||||
"justify-content",
|
||||
"align-items",
|
||||
"float",
|
||||
"clear",
|
||||
"overflow",
|
||||
"overflow-x",
|
||||
"overflow-y",
|
||||
"padding",
|
||||
"padding-top",
|
||||
"padding-right",
|
||||
"padding-bottom",
|
||||
"padding-left",
|
||||
"margin",
|
||||
"margin-top",
|
||||
"margin-right",
|
||||
"margin-bottom",
|
||||
"margin-left",
|
||||
"width",
|
||||
"min-width",
|
||||
"max-width",
|
||||
"height",
|
||||
"min-height",
|
||||
"max-height",
|
||||
"font-size",
|
||||
"font-family",
|
||||
"text-align",
|
||||
"text-justify",
|
||||
"text-indent",
|
||||
"text-overflow",
|
||||
"text-decoration",
|
||||
"white-space",
|
||||
"color",
|
||||
"background",
|
||||
"background-position",
|
||||
"background-repeat",
|
||||
"background-size",
|
||||
"background-color",
|
||||
"background-clip",
|
||||
"border",
|
||||
"border-style",
|
||||
"border-width",
|
||||
"border-color",
|
||||
"border-top-style",
|
||||
"border-top-width",
|
||||
"border-top-color",
|
||||
"border-right-style",
|
||||
"border-right-width",
|
||||
"border-right-color",
|
||||
"border-bottom-style",
|
||||
"border-bottom-width",
|
||||
"border-bottom-color",
|
||||
"border-left-style",
|
||||
"border-left-width",
|
||||
"border-left-color",
|
||||
"border-radius",
|
||||
"opacity",
|
||||
"filter",
|
||||
"list-style",
|
||||
"outline",
|
||||
"visibility",
|
||||
"box-shadow",
|
||||
"text-shadow",
|
||||
"resize",
|
||||
"transition"
|
||||
]
|
||||
}
|
||||
}
|
37
tsconfig.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"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,
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"types": ["@dcloudio/types", "@uni-helper/uni-app-types"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"types/**/*.d.ts",
|
||||
"types/**/*.ts"
|
||||
],
|
||||
"exclude": ["dist", "node_modules", "uni_modules"],
|
||||
// uni-app Component type prompt
|
||||
"vueCompilerOptions": {
|
||||
"nativeTags": ["block", "component", "template", "slot"]
|
||||
}
|
||||
}
|
||||
|
288
types/auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,288 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const createPinia: typeof import('pinia')['createPinia']
|
||||
const customRef: typeof import('vue')['customRef']
|
||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||
const defineComponent: typeof import('vue')['defineComponent']
|
||||
const defineStore: typeof import('pinia')['defineStore']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const h: typeof import('vue')['h']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const mapActions: typeof import('pinia')['mapActions']
|
||||
const mapGetters: typeof import('pinia')['mapGetters']
|
||||
const mapState: typeof import('pinia')['mapState']
|
||||
const mapStores: typeof import('pinia')['mapStores']
|
||||
const mapWritableState: typeof import('pinia')['mapWritableState']
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
|
||||
const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress']
|
||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||
const onError: typeof import('@dcloudio/uni-app')['onError']
|
||||
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||
const onHide: typeof import('@dcloudio/uni-app')['onHide']
|
||||
const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch']
|
||||
const onLoad: typeof import('@dcloudio/uni-app')['onLoad']
|
||||
const onMounted: typeof import('vue')['onMounted']
|
||||
const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']
|
||||
const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']
|
||||
const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']
|
||||
const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']
|
||||
const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']
|
||||
const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound']
|
||||
const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll']
|
||||
const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh']
|
||||
const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom']
|
||||
const onReady: typeof import('@dcloudio/uni-app')['onReady']
|
||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||
const onResize: typeof import('@dcloudio/uni-app')['onResize']
|
||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||
const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage']
|
||||
const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline']
|
||||
const onShow: typeof import('@dcloudio/uni-app')['onShow']
|
||||
const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap']
|
||||
const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange']
|
||||
const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection']
|
||||
const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const reactive: typeof import('vue')['reactive']
|
||||
const readonly: typeof import('vue')['readonly']
|
||||
const ref: typeof import('vue')['ref']
|
||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const storeToRefs: typeof import('pinia')['storeToRefs']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const toValue: typeof import('vue')['toValue']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const unref: typeof import('vue')['unref']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
const useCssVars: typeof import('vue')['useCssVars']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
const watch: typeof import('vue')['watch']
|
||||
const watchEffect: typeof import('vue')['watchEffect']
|
||||
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
// for vue template auto import
|
||||
import { UnwrapRef } from 'vue'
|
||||
declare module 'vue' {
|
||||
interface ComponentCustomProperties {
|
||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
|
||||
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
|
||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
|
||||
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
|
||||
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
|
||||
readonly mapStores: UnwrapRef<typeof import('pinia')['mapStores']>
|
||||
readonly mapWritableState: UnwrapRef<typeof import('pinia')['mapWritableState']>
|
||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||
readonly onAddToFavorites: UnwrapRef<typeof import('@dcloudio/uni-app')['onAddToFavorites']>
|
||||
readonly onBackPress: UnwrapRef<typeof import('@dcloudio/uni-app')['onBackPress']>
|
||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||
readonly onError: UnwrapRef<typeof import('@dcloudio/uni-app')['onError']>
|
||||
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||
readonly onHide: UnwrapRef<typeof import('@dcloudio/uni-app')['onHide']>
|
||||
readonly onLaunch: UnwrapRef<typeof import('@dcloudio/uni-app')['onLaunch']>
|
||||
readonly onLoad: UnwrapRef<typeof import('@dcloudio/uni-app')['onLoad']>
|
||||
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||
readonly onNavigationBarButtonTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']>
|
||||
readonly onNavigationBarSearchInputChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']>
|
||||
readonly onNavigationBarSearchInputClicked: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']>
|
||||
readonly onNavigationBarSearchInputConfirmed: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']>
|
||||
readonly onNavigationBarSearchInputFocusChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']>
|
||||
readonly onPageNotFound: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageNotFound']>
|
||||
readonly onPageScroll: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageScroll']>
|
||||
readonly onPullDownRefresh: UnwrapRef<typeof import('@dcloudio/uni-app')['onPullDownRefresh']>
|
||||
readonly onReachBottom: UnwrapRef<typeof import('@dcloudio/uni-app')['onReachBottom']>
|
||||
readonly onReady: UnwrapRef<typeof import('@dcloudio/uni-app')['onReady']>
|
||||
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||
readonly onResize: UnwrapRef<typeof import('@dcloudio/uni-app')['onResize']>
|
||||
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||
readonly onShareAppMessage: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareAppMessage']>
|
||||
readonly onShareTimeline: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareTimeline']>
|
||||
readonly onShow: UnwrapRef<typeof import('@dcloudio/uni-app')['onShow']>
|
||||
readonly onTabItemTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onTabItemTap']>
|
||||
readonly onThemeChange: UnwrapRef<typeof import('@dcloudio/uni-app')['onThemeChange']>
|
||||
readonly onUnhandledRejection: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnhandledRejection']>
|
||||
readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']>
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
||||
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||
readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
|
||||
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||
}
|
||||
}
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
|
||||
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
|
||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
|
||||
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
|
||||
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
|
||||
readonly mapStores: UnwrapRef<typeof import('pinia')['mapStores']>
|
||||
readonly mapWritableState: UnwrapRef<typeof import('pinia')['mapWritableState']>
|
||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||
readonly onAddToFavorites: UnwrapRef<typeof import('@dcloudio/uni-app')['onAddToFavorites']>
|
||||
readonly onBackPress: UnwrapRef<typeof import('@dcloudio/uni-app')['onBackPress']>
|
||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||
readonly onError: UnwrapRef<typeof import('@dcloudio/uni-app')['onError']>
|
||||
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||
readonly onHide: UnwrapRef<typeof import('@dcloudio/uni-app')['onHide']>
|
||||
readonly onLaunch: UnwrapRef<typeof import('@dcloudio/uni-app')['onLaunch']>
|
||||
readonly onLoad: UnwrapRef<typeof import('@dcloudio/uni-app')['onLoad']>
|
||||
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||
readonly onNavigationBarButtonTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']>
|
||||
readonly onNavigationBarSearchInputChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']>
|
||||
readonly onNavigationBarSearchInputClicked: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']>
|
||||
readonly onNavigationBarSearchInputConfirmed: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']>
|
||||
readonly onNavigationBarSearchInputFocusChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']>
|
||||
readonly onPageNotFound: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageNotFound']>
|
||||
readonly onPageScroll: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageScroll']>
|
||||
readonly onPullDownRefresh: UnwrapRef<typeof import('@dcloudio/uni-app')['onPullDownRefresh']>
|
||||
readonly onReachBottom: UnwrapRef<typeof import('@dcloudio/uni-app')['onReachBottom']>
|
||||
readonly onReady: UnwrapRef<typeof import('@dcloudio/uni-app')['onReady']>
|
||||
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||
readonly onResize: UnwrapRef<typeof import('@dcloudio/uni-app')['onResize']>
|
||||
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||
readonly onShareAppMessage: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareAppMessage']>
|
||||
readonly onShareTimeline: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareTimeline']>
|
||||
readonly onShow: UnwrapRef<typeof import('@dcloudio/uni-app')['onShow']>
|
||||
readonly onTabItemTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onTabItemTap']>
|
||||
readonly onThemeChange: UnwrapRef<typeof import('@dcloudio/uni-app')['onThemeChange']>
|
||||
readonly onUnhandledRejection: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnhandledRejection']>
|
||||
readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']>
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
||||
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||
readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
|
||||
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||
}
|
||||
}
|
14
types/components.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
PageNav: typeof import('./../src/components/page-nav/page-nav.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
27
types/global.d.ts
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
import type { VNodeChild } from 'vue';
|
||||
|
||||
declare global {
|
||||
// vue
|
||||
declare type VueNode = VNodeChild | JSX.Element;
|
||||
|
||||
declare type TimeoutHandle = ReturnType<typeof setTimeout>;
|
||||
declare type IntervalHandle = ReturnType<typeof setInterval>;
|
||||
|
||||
interface ImportMetaEnv extends ViteEnv {
|
||||
__: unknown;
|
||||
}
|
||||
|
||||
declare interface ViteEnv {
|
||||
VITE_APP_TITLE?: string;
|
||||
VITE_APP_BASE_API: string;
|
||||
VITE_DROP_CONSOLE: Boolean;
|
||||
}
|
||||
|
||||
declare function parseInt(s: string | number, radix?: number): number;
|
||||
|
||||
declare function parseFloat(string: string | number): number;
|
||||
|
||||
declare interface Uni {
|
||||
$u: any
|
||||
}
|
||||
}
|
10
types/module.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue';
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
declare module 'uview-plus';
|
||||
declare module 'uview-plus/libs/mixin/mpShare.js';
|
57
uno.config.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import type { Preset, SourceCodeTransformer } from 'unocss';
|
||||
import {
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetUno,
|
||||
transformerDirectives,
|
||||
transformerVariantGroup,
|
||||
} from 'unocss';
|
||||
import {
|
||||
presetApplet,
|
||||
presetRemRpx,
|
||||
transformerApplet,
|
||||
transformerAttributify,
|
||||
} from 'unocss-applet';
|
||||
|
||||
// eslint-disable-next-line n/prefer-global/process
|
||||
const isApplet = process.env?.UNI_PLATFORM?.startsWith('mp-') ?? false;
|
||||
const presets: Preset[] = [];
|
||||
const transformers: SourceCodeTransformer[] = [];
|
||||
|
||||
if (isApplet) {
|
||||
/**
|
||||
* UnoCSS Applet
|
||||
* @see https://github.com/unocss-applet/unocss-applet
|
||||
*/
|
||||
presets.push(presetApplet());
|
||||
presets.push(presetRemRpx()); // 如果需要使用 rem 转 rpx 单位,需要启用此插件
|
||||
transformers.push(transformerAttributify({ ignoreAttributes: ['block'] }));
|
||||
transformers.push(transformerApplet());
|
||||
} else {
|
||||
presets.push(presetUno());
|
||||
presets.push(presetAttributify());
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
presets,
|
||||
/**
|
||||
* 自定义快捷语句
|
||||
* @see https://github.com/unocss/unocss#shortcuts
|
||||
*/
|
||||
shortcuts: [
|
||||
['center', 'flex justify-center items-center'],
|
||||
[
|
||||
'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',
|
||||
],
|
||||
],
|
||||
transformers: [
|
||||
transformerDirectives(), // 启用 @apply 功能
|
||||
transformerVariantGroup(), // 启用 () 分组功能
|
||||
...transformers,
|
||||
],
|
||||
});
|
27
vite.config.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { resolve } from 'node:path';
|
||||
import { defineConfig } from 'vite';
|
||||
import type { UserConfig } from 'vite';
|
||||
import createVitePlugins from './build/vite/plugins';
|
||||
import proxy from './build/vite/proxy';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig((): UserConfig => {
|
||||
const isBuild = process.env.NODE_ENV === 'production';
|
||||
return {
|
||||
resolve: {
|
||||
// https://cn.vitejs.dev/config/#resolve-alias
|
||||
alias: {
|
||||
// 设置别名
|
||||
'@': resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
// vite 相关配置
|
||||
server: {
|
||||
port: 8080,
|
||||
host: true,
|
||||
open: true,
|
||||
proxy,
|
||||
},
|
||||
plugins: createVitePlugins(isBuild),
|
||||
};
|
||||
});
|