feat: add a new feature

This commit is contained in:
KuroSago 2024-06-21 10:04:26 +08:00
parent 1ffc4237ca
commit b44e90451d
25 changed files with 822 additions and 246 deletions

29
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,29 @@
/*
* Filename: /Users/kurosago/Code/Self/uniViteTemplate/.vscode/launch.json
* Path: /Users/kurosago/Code/Self/uniViteTemplate
* Created Date: Thursday, June 20th 2024, 9:26:43 pm
* Author: KuroSago
*
* Copyright (c) 2024 self.
*/
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Vue Debugger",
"url": "http://localhost:9527",
"webRoot": "${workspaceFolder}"
},
{
"type": "node",
"request": "launch",
"name": "TS Debugger",
"runtimeExecutable": "tsx",
"skipFiles": ["<node_internals>/**", "${workspaceFolder}/node_modules/**"],
"program": "${file}"
}
]
}

View File

@ -4,7 +4,6 @@
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"eslint.experimental.useFlatConfig": true,
"editor.formatOnSave": false,
"prettier.enable": false,
"unocss.root": ["./"],

View File

@ -39,7 +39,7 @@ export default antfu({
'./dist/*',
'./.vscode/*',
'./.idea/*',
'**/androidPrivacy.json',
'./src/androidPrivacy.json',
'README.md',
],
});

View File

@ -5,7 +5,6 @@
"message": "__请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款, 包括但不限于:为了更好的向你提供服务, 我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/>__你可阅读<a href=\"\">《服务协议》</a>和<a href=\"\">《隐私政策》</a>了解详细信息。如果你同意, 请点击下面按钮开始接受我们的服务。",
"buttonAccept": "同意并接受",
"buttonRefuse": "暂不同意",
// HX 3.4.13, system 使webview , 使uni-appweb
"hrefLoader": "default",
"second": {
"title": "确认提示",

23
src/enums/UI.ts Normal file
View File

@ -0,0 +1,23 @@
// S、M、L、XL、XXL、XXXL
// S: Small 小 M: Middle 中L: Large 大XL: extra large 加大多个X则代表多重加大码X越多码数越大。
export enum Size {
S = 's',
M = 'm',
L = 'l',
XL = 'xl',
XXl = 'xxl',
XXXL = 'xxxl',
}
export enum UNOCSS_THEME_TYPE {
SCREENS = 'screens',
COLORS = 'colors',
SPACING = 'spacing',
BORDERRADIUS = 'borderRadius',
FONTFAMILY = 'fontFamily',
}
export enum THEME_MODEL {
LIGHT = 'light',
DARK = 'dark',
}

10
src/enums/apiEnum.ts Normal file
View File

@ -0,0 +1,10 @@
export enum ApiVersionEnum {
V1 = 'v1',
}
export enum ApiPathEnum {
ADMIN = 'admin',
USERS = 'users',
PORTAL = 'portal',
RIDERS = 'riders',
}

View File

@ -1,5 +0,0 @@
// token key
export const TOKEN_KEY = 'TOKEN__';
// user info key
export const USER_INFO_KEY = 'USER__INFO__';

112
src/enums/platformEnum.ts Normal file
View File

@ -0,0 +1,112 @@
/**
*
*/
export enum PLATFORMS {
DEFAULT = 'DEFAULT' /* 默认 */,
VUE3 = 'VUE3' /* HBuilderX 3.2.0+ */,
APP_PLUS = 'APP-PLUS' /* App */,
APP_PLUS_NVUE = 'APP-PLUS-NVUE' /* App nvue 页面 */,
APP_NVUE = 'APP-NVUE' /* App nvue 页面 */,
H5 = 'H5' /* H5 */,
MP_WEIXIN = 'MP-WEIXIN' /* 微信小程序 */,
MP_ALIPAY = 'MP-ALIPAY' /* 支付宝小程序 */,
MP_BAIDU = 'MP_BAIDU' /* 百度小程序 */,
MP_TOUTIAO = 'MP-TOUTIAO' /* 字节跳动小程序 */,
MP_LARK = 'MP-LARK' /* 飞书小程序 */,
MP_QQ = 'MP-QQ' /* QQ小程序 */,
MP_KUAISHOU = 'MP-KUAISHOU' /* 快手小程序 */,
MP_JD = 'MP-JD' /* 京东小程序 */,
MP_360 = 'MP-360' /* 360小程序 */,
MP = 'MP' /* 微信小程序/支付宝小程序/百度小程序/字节跳动小程序/飞书小程序/QQ小程序/360小程序 */,
QUICKAPP_WEBVIEW = 'QUICKAPP-WEBVIEW' /* 快应用通用(包含联盟、华为) */,
QUICKAPP_WEBVIEW_UNION = 'QUICKAPP-WEBVIEW-UNION' /* 快应用联盟 */,
QUICKAPP_WEBVIEW_HUAWEI = 'QUICKAPP-WEBVIEW-HUAWEI' /* 快应用华为 */,
}
/**
*
* @constructor
*/
function PLATFORM_ENV() {
let platform = PLATFORMS.DEFAULT;
/* #ifdef VUE3 */
platform = PLATFORMS.VUE3;
/* #endif */
/* #ifdef APP-PLUS */
platform = PLATFORMS.APP_PLUS;
/* #endif */
/* #ifdef APP-PLUS-NVUE */
platform = PLATFORMS.APP_PLUS_NVUE;
/* #endif */
/* #ifdef APP-NVUE */
platform = PLATFORMS.APP_NVUE;
/* #endif */
/* #ifdef H5 */
platform = PLATFORMS.H5;
/* #endif */
/* #ifdef MP */
platform = PLATFORMS.MP;
/* #endif */
/* #ifdef MP-WEIXIN */
platform = PLATFORMS.MP_WEIXIN;
/* #endif */
/* #ifdef MP-ALIPAY */
platform = PLATFORMS.MP_ALIPAY;
/* #endif */
/* #ifdef MP_BAIDU */
platform = PLATFORMS.MP_BAIDU;
/* #endif */
/* #ifdef MP-TOUTIAO */
platform = PLATFORMS.MP_TOUTIAO;
/* #endif */
/* #ifdef MP-LARK */
platform = PLATFORMS.MP_LARK;
/* #endif */
/* #ifdef MP-QQ */
platform = PLATFORMS.MP_QQ;
/* #endif */
/* #ifdef MP-KUAISHOU */
platform = PLATFORMS.MP_KUAISHOU;
/* #endif */
/* #ifdef MP-JD */
platform = PLATFORMS.MP_JD;
/* #endif */
/* #ifdef MP-360 */
platform = PLATFORMS.MP_360;
/* #endif */
/* #ifdef QUICKAPP-WEBVIEW */
platform = PLATFORMS.QUICKAPP_WEBVIEW;
/* #endif */
/* #ifdef QUICKAPP-WEBVIEW-UNION */
platform = PLATFORMS.QUICKAPP_WEBVIEW_UNION;
/* #endif */
/* #ifdef QUICKAPP-WEBVIEW-HUAWEI */
platform = PLATFORMS.QUICKAPP_WEBVIEW_HUAWEI;
/* #endif */
return platform;
}
/**
*
* "process.env['UNI_PLATFORM']"
*/
export const CURRENT_PLATFORM = PLATFORM_ENV();

View File

@ -0,0 +1,31 @@
/**
[
{"key":"userService","value":"用户服务协议"},
{"key":"privacyPolicy","value":"个人信息保护政策"},
{"key":"userPrivacy","value":"用户隐私政策"}
{"key":"platformRules","value":"平台规则"},
{"key":"platformQualification","value":"平台资质"}
{"key":"personalInformation","value":"个人信息收集清单"},
{"key":"thirdPartyInformation","value":"第三方信息共享清单"}
]
*/
export enum ProtocolsEnum {
userService = '用户服务协议',
privacyPolicy = '个人信息保护政策',
userPrivacy = '用户隐私政策',
platformRules = '平台规则',
platformQualification = '平台资质',
personalInformation = '个人信息收集清单',
thirdPartyInformation = '第三方信息共享清单',
}
export enum ProtocolsKey {
userService = 'user_service',
privacyPolicy = 'privacy_policy',
userPrivacy = 'user_privacy',
platformRules = 'platform_rules',
platformQualification = 'platform_qualification',
personalInformation = 'personal_information',
thirdPartyInformation = 'third_party_information',
}

13
src/enums/routerEnum.ts Normal file
View File

@ -0,0 +1,13 @@
export enum NAVIGATE_TYPE {
NAVIGATE_TO = 'navigateTo',
REDIRECT_TO = 'redirectTo',
RE_LAUNCH = 'reLaunch',
SWITCH_TAB = 'switchTab',
NAVIGATE_BACK = 'navigateBack',
}
export const NAVIGATE_TYPE_LIST = ['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'];
export const HOME_PAGE = '/pages/index/index';
export const LOGIN_PAGE = '/pages/login/index';
export const NOT_FOUND_PAGE = '/pages/notFound/404';

View File

@ -0,0 +1,14 @@
<!-- //@ts-nocheck -->
<template>
<view
id="layout"
:class="{
dark: __themeModel === 'dark',
}"
class="bg-page flex flex-col"
:style="__theme"
style="min-height: 100vh; transition-duration: 1000ms; transition: all"
>
<!-- template -->
</view>
</template>

18
src/mixins/theme.ts Normal file
View File

@ -0,0 +1,18 @@
// 混入主题
import { useSystemInfoStore } from '@/stores/modules/system';
export const themeMixin = {
computed: {
__themeModel() {
const themeStore = storeToRefs(useSystemInfoStore());
return themeStore.themeModel.value;
},
__theme() {
const themeStore = storeToRefs(useSystemInfoStore());
const theme = themeStore.theme.value;
return Object.keys(theme)
.map(key => `${key}:${theme[key]};`)
.join('');
},
},
};

46
src/model/theme.ts Normal file
View File

@ -0,0 +1,46 @@
export interface ThemeCfg {
'--colors-primary': string
'--colors-secondary': string
'--colors-accent': string
'--colors-success': string
'--colors-warning': string
'--colors-error': string
'--colors-disable': string
'--colors-danger': string
'--colors-mark': string
'--colors-title': string
'--colors-subtitle': string
'--colors-paragraph': string
'--colors-fontColorblack': string
'--colors-fontColorPrimary': string
'--colors-fontColorInverse': string
'--colors-fontColorGrey': string
'--colors-fontColorPlaceholder': string
'--colors-fontColorDisable': string
'--colors-fontColorBottomText': string
'--colors-container': string
'--colors-page': string
'--colors-containerInverse': string
'--colors-containerHover': string
'--colors-containerMask': string
'--colors-iconButton': string
'--colors-borderColor': string
'--fontSize-sm': string
'--fontSize-base': string
'--fontSize-lg': string
'--fontSize-title': string
'--fontSize-subtitle': string
'--fontSize-paragraph': string
'--borderRadius-sm': string
'--borderRadius-base': string
'--borderRadius-lg': string
'--borderRadius-circle': string
'--spacing-rowSm': string
'--spacing-rowBase': string
'--spacing-rowLg': string
'--spacing-colSm': string
'--spacing-colBase': string
'--spacing-colLg': string
'--opacity-disabled': string
[propName: string]: string
}

View File

@ -0,0 +1,47 @@
import { THEME_MODEL } from '@/enums/UI';
import type { ThemeCfg } from '@/model/theme';
import { theme as _theme } from '@/theme/theme';
interface SystemInfo {
theme: ThemeCfg
themeModel: THEME_MODEL
systemInfo: UniApp.GetSystemInfoResult
}
export const useSystemInfoStore = defineStore({
id: 'system',
state: (): SystemInfo => ({
// 主题
// theme: <ThemeCfg>Object.assign(_theme.light),
theme: <ThemeCfg>{ ..._theme.light },
// 当前主题模式
themeModel: THEME_MODEL.LIGHT as THEME_MODEL,
// 系统信息
systemInfo: {
statusBarHeight: 0,
} as UniApp.GetSystemInfoResult,
}),
getters: {},
actions: {
// 获取系统信息
async getSystemInfo() {
const res = (await uni.getSystemInfoSync()) as UniApp.GetSystemInfoResult;
this.systemInfo = res;
},
// 切换主题
changeThemeModel({ model = THEME_MODEL.DARK as THEME_MODEL }) {
this.themeModel = model;
this.theme = { ..._theme[model] };
},
},
persist: {
storage: {
setItem: uni.setStorageSync,
getItem: uni.getStorageSync,
},
paths: ['theme', 'themeModel', 'systemInfo'],
},
});

205
src/theme/theme.ts Normal file
View File

@ -0,0 +1,205 @@
/**
*
* @note : ( key )
* --
* colors
* primary
* key
* key , bug
*/
// container {
// // ThemeColor
// '--source': '#00bdd4',
// }
/**
* secondary
* accent
* tertiary 线
* neutral
*/
export const theme = {
light: {
/* light */
/* 颜色 */
'--colors-primary': '#FFE274', // 主色
'--colors-secondary': '#5A7767', // 次要
'--colors-accent': '#FB9300', // 强调
'--colors-success': '#4cd964',
'--colors-warning': '#f0ad4e',
'--colors-error': '#dd524d',
'--colors-disable': '#ddd',
'--colors-danger': '#FF6843',
'--colors-mark': 'rgba(0,0,0,0.4)',
/* 文章场景相关 */
'--colors-title': '#2c405a', // 文章标题颜色
'--colors-subtitle': '#555555', // 二级标题颜色
'--colors-paragraph': '#3f536e', // 文章段落颜色
/* 文字基本颜色 */
'--colors-fontColorblack': '#333', // 基本色 - 不切换
'--colors-fontColorPrimary': '#333', // 基本色
'--colors-fontColorInverse': '#fff', // 反色
'--colors-fontColorGrey': '#999', // 辅助灰色,如加载更多的提示信息
'--colors-fontColorPlaceholder': '#808080',
'--colors-fontColorDisable': '#c0c0c0',
'--colors-fontColorBottomText': '#6F6E80', // 底部tabbar文字颜色
/* 背景颜色 */
'--colors-container': '#ffffff',
'--colors-page': '#F6F4F0',
'--colors-containerInverse': '#000000', // 背景反色
'--colors-containerHover': 'rgba(255, 226, 116, .5)', // 点击状态颜色
'--colors-secondaryHover': 'rgba(90,119,103, .5)', // 点击状态颜色
'--colors-containerMask': 'rgba(0, 0, 0, 0.4)', // 遮罩颜色
// '--colors-iconButton': '#F5F5EC',
'--colors-iconButton': '#E4E4E4', // ICON 按钮背景色
/* 边框颜色 */
'--colors-borderColor': '#c8c7cc',
/* 文字尺寸 */
'--fontSize-sm': '24rpx',
'--fontSize-base': '28rpx',
'--fontSize-lg': '32rpx',
/* 文章场景相关 */
'--fontSize-title': '40rpx',
'--fontSize-subtitle': '36rpx',
'--fontSize-paragraph': '32rpx',
/* Border Radius */
'--borderRadius-sm': '4rpx',
'--borderRadius-base': '6rpx',
'--borderRadius-lg': '12rpx',
'--borderRadius-circle': '50%',
/* 水平间距 */
'--spacing-rowSm': '12px',
'--spacing-rowBase': '24rpx',
'--spacing-rowLg': '32rpx',
/* 垂直间距 */
'--spacing-colSm': '8rpx',
'--spacing-colBase': '16rpx',
'--spacing-colLg': '24rpx',
/* 透明度 */
'--opacity-disabled': '0.3', // 组件禁用态的透明度
},
dark: {
/* dark */
/* 颜色 */
'--colors-primary': '#FFE274',
'--colors-secondary': '#5A7767', // 次要
'--colors-accent': '#FB9300', // 强调
'--colors-success': '#4cd964',
'--colors-warning': '#f0ad4e',
'--colors-error': '#dd524d',
'--colors-disable': '#ddd',
'--colors-danger': '#FF6843',
'--colors-mark': 'rgba(26, 26, 26,0.4)',
/* 文章场景相关 */
'--colors-title': '#2c405a', // 文章标题颜色
'--colors-subtitle': '#555555', // 二级标题颜色
'--colors-paragraph': '#3f536e', // 文章段落颜色
/* 文字基本颜色 */
'--colors-fontColorblack': '#333', // 基本色 - 不切换
'--colors-fontColorPrimary': '#999', // 基本色
'--colors-fontColorInverse': '#333', // 反色
'--colors-fontColorGrey': '#666', // 辅助灰色,如加载更多的提示信息
'--colors-fontColorPlaceholder': '#808080',
'--colors-fontColorDisable': '#c0c0c0',
'--colors-fontColorBottomText': '#6F6E80', // 底部tabbar文字颜色
/* 背景颜色 */
'--colors-container': '#000000',
'--colors-page': '#191919',
'--colors-containerInverse': '#ffffff', // 背景反色
'--colors-containerHover': 'rgba(255, 226, 116, .5)', // 点击状态颜色
'--colors-secondaryHover': 'rgba(90,119,103, .5)', // 点击状态颜色
'--colors-containerMask': 'rgba(0, 0, 0, 0.4)', // 遮罩颜色
'--colors-iconButton': '#808080', // ICON 按钮背景色
/* 边框颜色 */
'--colors-borderColor': '#c8c7cc',
/* 文字尺寸 */
'--fontSize-sm': '24rpx',
'--fontSize-base': '28rpx',
'--fontSize-lg': '32rpx',
/* 文章场景相关 */
'--fontSize-title': '40rpx',
'--fontSize-subtitle': '36rpx',
'--fontSize-paragraph': '32rpx',
/* Border Radius */
'--borderRadius-sm': '4rpx',
'--borderRadius-base': '6rpx',
'--borderRadius-lg': '12rpx',
'--borderRadius-circle': '50%',
/* 水平间距 */
'--spacing-rowSm': '12px',
'--spacing-rowBase': '24rpx',
'--spacing-rowLg': '32rpx',
/* 垂直间距 */
'--spacing-colSm': '8rpx',
'--spacing-colBase': '16rpx',
'--spacing-colLg': '24rpx',
/* 透明度 */
'--opacity-disabled': '0.3', // 组件禁用态的透明度
},
};
enum ExtendStyle {
SPACING = 'spacing',
// BORDERRADIUS = 'borderRadius' ,
// FONTSIZE = 'fontSize',
OPACITY = 'opacity',
}
// 构建 theme
export function buildTheme() {
const _theme: any = {
extend: {},
};
const flattenedObj: Map<string, any> = new Map([]);
for (const [_, themeItem] of Object.entries(theme)) {
for (const [key, _] of Object.entries(themeItem)) {
flattenedObj.set(key, _);
}
}
const extendList = [ExtendStyle.OPACITY];
flattenedObj.forEach((_v: string, k: string) => {
// const [_, key, value] = k.match(/^--(.+?)-(.+)$/) || [0, 'key', 'value'];
const [_, key, value] = k.match(/^--([^-]+)-(.+)$/) || [0, 'key', 'value'];
if (extendList.includes(key as ExtendStyle)) {
if (!_theme.extend[key]) {
_theme.extend[key] = {};
}
_theme.extend[key][value] = `var(${k})`;
} else {
if (!_theme[key]) {
_theme[key] = {};
}
_theme[key][value] = `var(${k})`;
}
});
// console.log('_theme ===>>>', _theme);
return _theme;
}

16
src/types/import-components.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
AppProvider: typeof import('./../components/AppProvider/index.vue')['default']
BasicButton: typeof import('./../components/BasicButton/index.vue')['default']
BasicInput: typeof import('./../components/BasicInput/index.vue')['default']
Iconify: typeof import('./../components/Iconify/index.vue')['default']
Test: typeof import('./../components/Test/index.vue')['default']
}
}

View File

@ -1,26 +0,0 @@
import { getCache, setCache } from '@/utils/cache';
import { TOKEN_KEY } from '@/enums/cacheEnum';
const authenticationScheme = 'Bearer';
export function getToken() {
return getCache<string>(TOKEN_KEY) || null;
}
export function getAuthorization() {
const token = getToken();
return token ? `${authenticationScheme} ${token}` : null;
}
export function setToken(token: string) {
return setCache(TOKEN_KEY, token);
}
export function removeToken() {
return setCache(TOKEN_KEY, null);
}
// 是否登录
export function isLogin() {
return !!getToken();
}

View File

@ -1,29 +0,0 @@
import type { CreateStorageParams } from './storageCache';
import { createStorage } from './storageCache';
import { DEFAULT_CACHE_TIME, DEFAULT_PREFIX_KEY, cacheCipher, enableStorageEncryption } from '@/settings/encryptionSetting';
const options: Partial<CreateStorageParams> = {
prefixKey: DEFAULT_PREFIX_KEY,
key: cacheCipher.key,
iv: cacheCipher.iv,
hasEncrypt: enableStorageEncryption,
timeout: DEFAULT_CACHE_TIME,
};
export const storage = createStorage(options);
export function setCache(key: string, value: any, expire?: number | null): void {
storage.set(key, value, expire);
}
export function getCache<T = any>(key: string): T {
return storage.get<T>(key);
}
export function removeCache(key: string): void {
return storage.remove(key);
}
export function clearCache(): void {
return storage.clear();
}

View File

@ -1,110 +0,0 @@
import { cacheCipher } from '@/settings/encryptionSetting';
import type { EncryptionParams } from '@/utils/cipher';
import { AesEncryption } from '@/utils/cipher';
import { isNullOrUnDef } from '@/utils/is';
export interface CreateStorageParams extends EncryptionParams {
prefixKey: string
hasEncrypt: boolean
timeout?: number | null
}
export function createStorage({
prefixKey = '',
key = cacheCipher.key,
iv = cacheCipher.iv,
timeout = null,
hasEncrypt = true,
}: Partial<CreateStorageParams> = {}) {
if (hasEncrypt && [key.length, iv.length].some(item => item !== 16)) {
throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!');
}
const encryption = new AesEncryption({ key, iv });
/**
* Cache class
* Construction parameters can be passed into sessionStorage, localStorage,
* @class Cache
* @example
*/
class Storage {
private prefixKey?: string;
private encryption: AesEncryption;
private hasEncrypt: boolean;
constructor() {
this.prefixKey = prefixKey;
this.encryption = encryption;
this.hasEncrypt = hasEncrypt;
}
private getKey(key: string) {
return `${this.prefixKey}${key}`.toUpperCase();
}
/**
* Set cache
* @param {string} key
* @param {*} value
* @param {*} expire Expiration time in seconds
* @memberof Cache
*/
set(key: string, value: any, expire: number | null = timeout) {
try {
const stringData = JSON.stringify({
value,
time: Date.now(),
expire: !isNullOrUnDef(expire) ? new Date().getTime() + expire * 1000 : null,
});
const stringifyValue = this.hasEncrypt ? this.encryption.encryptByAES(stringData) : stringData;
uni.setStorageSync(this.getKey(key), stringifyValue);
} catch (err) {
throw new Error(`setStorageSync error: ${err}`);
}
}
/**
* Read cache
* @param {string} key
* @param {*} def
* @memberof Cache
*/
get<T = any>(key: string, def: any = null): T {
const val = uni.getStorageSync(this.getKey(key));
if (!val)
return def;
try {
const decVal = this.hasEncrypt ? this.encryption.decryptByAES(val) : val;
const data = JSON.parse(decVal);
const { value, expire } = data;
if (isNullOrUnDef(expire) || expire < new Date().getTime()) {
this.remove(key);
return def;
}
return value;
} catch (e) {
return def;
}
}
/**
* Delete cache based on key
* @param {string} key
* @memberof Cache
*/
remove(key: string) {
uni.removeStorageSync(this.getKey(key));
}
/**
* Delete all caches of this instance
*/
clear(): void {
uni.clearStorageSync();
}
}
return new Storage();
}

View File

@ -1,73 +0,0 @@
/**
* unocss defineConfig
* @link unocss: https://github.com/unocss/unocss
* @type {import('unocss').UserConfig}
*/
import { defineConfig, presetIcons } from 'unocss';
import presetWeapp from 'unocss-preset-weapp';
import { transformerAttributify, transformerClass } from 'unocss-preset-weapp/transformer';
const transformRules = {
'.': '-d2e-',
'/': '-s2f-',
':': '-c3a-',
'%': '-p25-',
'!': '-e21-',
'#': '-w23-',
'(': '-b28-',
')': '-b29-',
'[': '-f4b-',
']': '-f5d-',
'$': '-r24-',
',': '-r2c-',
};
const prefix = '';
export default defineConfig({
presets: [
// https://github.com/MellowCo/unocss-preset-weapp
presetWeapp({
nonValuedAttribute: true,
prefix,
whRpx: true,
transform: true,
platform: 'uniapp',
transformRules,
}),
presetIcons({
scale: 1.2,
warn: true,
}),
],
shortcuts: [
{
center: 'flex justify-center items-center',
},
],
theme: {
colors: {
primary: '#007AFF',
secondary: '#4CD964',
danger: '#FF3B30',
warning: '#FF9500',
info: '#5AC8FA',
light: '#F0F0F0',
dark: '#1A1A1A',
},
fontSize: {
mini: ['20rpx', '26rpx'],
},
},
transformers: [
transformerAttributify({
classPrefix: prefix,
transformRules,
nonValuedAttribute: true,
}),
transformerClass({
transformRules,
}),
],
});

97
unocss.config.ts Normal file
View File

@ -0,0 +1,97 @@
/**
* unocss defineConfig
* @link unocss: https://github.com/unocss/unocss
* @link unocss-preset-weapp: https://github.com/MellowCo/unocss-preset-weapp
*/
import presetWeapp from 'unocss-preset-weapp';
import { extractorAttributify, transformerClass } from 'unocss-preset-weapp/transformer';
import { presetIcons } from 'unocss';
import transformerDirectives from '@unocss/transformer-directives';
const { presetWeappAttributify, transformerAttributify } = extractorAttributify();
export default {
presets: [
// https://github.com/MellowCo/unocss-preset-weapp
presetWeapp(),
// attributify autocomplete
presetWeappAttributify(),
presetIcons(),
],
shortcuts: [
{
'border-base': 'border border-gray-500_10',
'center': 'flex justify-center items-center',
},
],
transformers: [
transformerDirectives({
enforce: 'pre',
}),
// https://github.com/MellowCo/unocss-preset-weapp/tree/main/src/transformer/transformerAttributify
transformerAttributify(),
// https://github.com/MellowCo/unocss-preset-weapp/tree/main/src/transformer/transformerClass
transformerClass(),
],
theme: {
extend: {
opacity: {
disabled: 'var(--opacity-disabled)',
},
},
colors: {
primary: 'var(--colors-primary)',
secondary: 'var(--colors-secondary)',
accent: 'var(--colors-accent)',
success: 'var(--colors-success)',
warning: 'var(--colors-warning)',
error: 'var(--colors-error)',
disable: 'var(--colors-disable)',
danger: 'var(--colors-danger)',
mark: 'var(--colors-mark)',
title: 'var(--colors-title)',
subtitle: 'var(--colors-subtitle)',
paragraph: 'var(--colors-paragraph)',
fontColorblack: 'var(--colors-fontColorblack)',
fontColorPrimary: 'var(--colors-fontColorPrimary)',
fontColorInverse: 'var(--colors-fontColorInverse)',
fontColorGrey: 'var(--colors-fontColorGrey)',
fontColorPlaceholder: 'var(--colors-fontColorPlaceholder)',
fontColorDisable: 'var(--colors-fontColorDisable)',
fontColorBottomText: 'var(--colors-fontColorBottomText)',
container: 'var(--colors-container)',
page: 'var(--colors-page)',
containerInverse: 'var(--colors-containerInverse)',
containerHover: 'var(--colors-containerHover)',
secondaryHover: 'var(--colors-secondaryHover)',
containerMask: 'var(--colors-containerMask)',
iconButton: 'var(--colors-iconButton)',
borderColor: 'var(--colors-borderColor)',
},
fontSize: {
sm: 'var(--fontSize-sm)',
base: 'var(--fontSize-base)',
lg: 'var(--fontSize-lg)',
title: 'var(--fontSize-title)',
subtitle: 'var(--fontSize-subtitle)',
paragraph: 'var(--fontSize-paragraph)',
},
borderRadius: {
sm: 'var(--borderRadius-sm)',
base: 'var(--borderRadius-base)',
lg: 'var(--borderRadius-lg)',
circle: 'var(--borderRadius-circle)',
},
spacing: {
rowSm: 'var(--spacing-rowSm)',
rowBase: 'var(--spacing-rowBase)',
rowLg: 'var(--spacing-rowLg)',
colSm: 'var(--spacing-colSm)',
colBase: 'var(--spacing-colBase)',
colLg: 'var(--spacing-colLg)',
},
},
};

View File

@ -0,0 +1,57 @@
import * as fs from 'node:fs';
import path from 'node:path';
import * as process from 'node:process';
import normallize from 'normalize-path';
import type { PluginOption } from 'vite';
// export const defaultPagesRE = /src[/\\]pages(.*)[/\\](.*)[/\\](.*)\.vue$/;
export const defaultPagesRE = /src[/\\]pages([^/\\]*)[/\\]([^/\\]*)[/\\](.*)\.vue$/;
export interface Options {
pagesRE: RegExp
name: string
configPath: string
pluginName: string
DEBUG: boolean
}
export default function (options: Partial<Options> = {}) {
const {
pagesRE = defaultPagesRE,
name = './src/layout/AppProvider.vue',
pluginName = 'AppProvider',
// DEBUG = process.env.DEBUG,
} = options;
const template = fs.readFileSync(normalizePagePathFromBase(name), 'utf-8');
return <PluginOption>{
name: pluginName,
enforce: 'pre',
transform(code, id) {
id = normalizePagePathFromBase(id);
const regResult = pagesRE.exec(id);
if (regResult && (regResult[2] === regResult[3] || regResult[3] === 'index')) {
const oldTempleate = /<template>([\s\S]*)<\/template>/.exec(code);
const tmp_string = oldTempleate != null ? oldTempleate[1] : '';
const newTemplate = template.replace('<!-- template -->', tmp_string as string);
code = code.replace(/<template>([\s\S]*)<\/template>/, newTemplate);
}
return { code, map: null };
},
};
function normalizePagePathFromBase(file: string) {
return normallize(path.relative(process.cwd(), file));
}
// function debug(...args) {
// DEBUG &&
// console.log(
// c.dim(new Date().toLocaleTimeString()),
// c.bold(c.red(`[debug:${pluginName}]`)),
// ...args,
// )
// }
}

View File

@ -0,0 +1,82 @@
import * as fs from 'node:fs';
import { buildTheme } from '../../src/theme/theme';
export default function buildThemeConfig() {
const _data = buildTheme();
// 写入时会先清空文件
fs.writeFile('./unocss.config.ts', `${createFileByTemplate(_data)}`, (err: any) => {
if (err) {
console.error(err);
} else {
console.log('构建 css 提示文件完成');
console.table(flatten(_data));
}
});
// 写完文件后使用编辑器格式化一下
}
function flatten(obj: any, parentKey = ''): any {
let result: any = {};
for (const [key, value] of Object.entries(obj)) {
const newKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof value === 'object' && value !== null) {
const flattened = flatten(value, newKey);
result = { ...result, ...flattened };
} else {
result[newKey] = value;
}
}
return result;
}
// 通过模版生成文件
function createFileByTemplate(theme: any) {
const fileString = `
/**
* unocss defineConfig
* @link unocss: https://github.com/unocss/unocss
* @link unocss-preset-weapp: https://github.com/MellowCo/unocss-preset-weapp
* */
import presetWeapp from 'unocss-preset-weapp'
import { extractorAttributify, transformerClass } from 'unocss-preset-weapp/transformer'
import { presetIcons } from 'unocss'
import transformerDirectives from '@unocss/transformer-directives'
const { presetWeappAttributify, transformerAttributify } = extractorAttributify()
export default {
presets: [
// https://github.com/MellowCo/unocss-preset-weapp
presetWeapp(),
// attributify autocomplete
presetWeappAttributify(),
presetIcons(),
],
shortcuts: [
{
'border-base': 'border border-gray-500_10',
'center': 'flex justify-center items-center',
},
],
transformers: [
transformerDirectives({
enforce: 'pre',
}),
// https://github.com/MellowCo/unocss-preset-weapp/tree/main/src/transformer/transformerAttributify
transformerAttributify(),
// https://github.com/MellowCo/unocss-preset-weapp/tree/main/src/transformer/transformerClass
transformerClass(),
],
theme: ${JSON.stringify(theme, null, 2)}
}
`;
return fileString;
}

View File

@ -0,0 +1,16 @@
import type { Options } from 'unplugin-vue-components/types';
import Components from 'unplugin-vue-components/vite';
export default function createComponents(options?: Options | undefined) {
return Components({
// 指定组件位置默认是src/components
dirs: ['src/components'],
deep: true, // search for subdirectories
// ui库解析器
// resolvers: [ElementPlusResolver()],
extensions: ['vue'],
// 配置文件生成位置
dts: 'src/types/import-components.d.ts',
...options,
});
}

5
vite/plugins/index.ts Normal file
View File

@ -0,0 +1,5 @@
import appProvider from './appProvider';
import buildThemeConfig from './build.unocss.config';
import createComponents from './components';
export { appProvider, buildThemeConfig, createComponents };