diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3b510aa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +max_line_length = 100 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7f0ebc --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Dependencies +node_modules + +# Build outputs +dist +dist-ssr +coverage +*.local + +# TypeScript +*.tsbuildinfo + +# ESLint +.eslintcache + +# OS +.DS_Store +Thumbs.db + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# WeChat MiniProgram +# npm 构建产物(通过微信开发者工具构建 npm 后生成) +miniprogram/miniprogram_npm/ + +# 微信开发者工具私有配置文件(包含个人开发环境配置) +project.private.config.json + +# 微信开发者工具临时文件 +*.wxapkg +*.wxss.map +*.wxml.map +*.js.map + +# Testing +# Cypress +/cypress/videos/ +/cypress/screenshots/ + +# Vitest +__screenshots__/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..fa345b3 --- /dev/null +++ b/.npmrc @@ -0,0 +1,5 @@ +# 设置包注册表镜像 +registry=https://registry.npmmirror.com/ + +# 设置 node-sass 二进制包的镜像(非常重要,能解决很多安装失败问题) +sass_binary_site=https://npmmirror.com/mirrors/node-sass/ \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..67a6a4e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,12 @@ +# 依赖目录 +node_modules/ +miniprogram_npm/ + +# 构建产物 +dist/ +coverage/ + +# 其他 +*.log +.DS_Store +package-lock.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..82b4a72 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "singleQuote": true, + "printWidth": 100, + "overrides": [ + { + "files": "*.wxml", + "options": { + "parser": "html" + } + }, + { + "files": "*.wxss", + "options": { + "parser": "css" + } + } + ] +} diff --git a/eslint.config.ts b/eslint.config.ts new file mode 100644 index 0000000..9a84235 --- /dev/null +++ b/eslint.config.ts @@ -0,0 +1,63 @@ +import eslint from '@eslint/js' +import tseslint from 'typescript-eslint' +import prettierConfig from 'eslint-config-prettier' +import prettierPlugin from 'eslint-plugin-prettier' + +export default tseslint.config( + // 全局忽略的文件和目录 + { + ignores: [ + '**/node_modules/**', + '**/miniprogram_npm/**', + '**/dist/**', + '**/coverage/**', + '**/*.js.map', + '**/typings/**', + '**/*.d.ts', + ], + }, + + // ESLint 推荐规则 + eslint.configs.recommended, + + // TypeScript ESLint 推荐规则 + ...tseslint.configs.recommended, + + // 项目特定配置 + { + files: ['**/*.ts'], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: './tsconfig.json', + }, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + prettier: prettierPlugin, + }, + rules: { + // Prettier 集成 + 'prettier/prettier': 'error', + + // TypeScript 规则调整(根据小程序特点) + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + + // 通用规则 + 'no-console': ['warn', { allow: ['warn', 'error'] }], + 'no-debugger': 'warn', + }, + }, + + // Prettier 配置(禁用与 Prettier 冲突的规则) + prettierConfig, +) diff --git a/miniprogram/api/login.ts b/miniprogram/api/login.ts new file mode 100644 index 0000000..a5a4481 --- /dev/null +++ b/miniprogram/api/login.ts @@ -0,0 +1,31 @@ +import request from '../utils/request' +import { StandardResponse } from './types' + +/** + * 登录接口请求参数类型 + */ +export interface LoginParams { + userName: string + password: string +} + +/** + * 用户信息数据类型 + */ +export interface UserInfoData { + id: number + username: string + nickname: string + avatar: string + email: string + phone: string +} + +/** + * 登录接口 + * @param params 登录参数 + * @returns 返回标准响应格式的登录数据 + */ +export const loginReq = (params: LoginParams) => { + return request.post>('/wechatLogin/wechatLogin.action', params) +} diff --git a/miniprogram/api/types.ts b/miniprogram/api/types.ts new file mode 100644 index 0000000..e0a092d --- /dev/null +++ b/miniprogram/api/types.ts @@ -0,0 +1,8 @@ +/** + * 标准响应数据格式 + */ +export interface StandardResponse { + code: number + data: T + message: string +} diff --git a/miniprogram/app.json b/miniprogram/app.json new file mode 100644 index 0000000..57bc196 --- /dev/null +++ b/miniprogram/app.json @@ -0,0 +1,14 @@ +{ + "pages": ["pages/date/date", "pages/login/login", "pages/index/index"], + "window": { + "navigationStyle": "default", + "navigationBarTextStyle": "black", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTitleText": "My Weapp Template" + }, + "style": "v2", + "renderer": "webview", + "componentFramework": "glass-easel", + "sitemapLocation": "sitemap.json", + "lazyCodeLoading": "requiredComponents" +} diff --git a/miniprogram/app.scss b/miniprogram/app.scss new file mode 100644 index 0000000..3662180 --- /dev/null +++ b/miniprogram/app.scss @@ -0,0 +1,18 @@ +/**app.wxss**/ +page, +view, +scroll-view, +image, +text { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +.container { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; +} diff --git a/miniprogram/app.ts b/miniprogram/app.ts new file mode 100644 index 0000000..25d3d1e --- /dev/null +++ b/miniprogram/app.ts @@ -0,0 +1,5 @@ +// app.ts +App({ + globalData: {}, + onLaunch() {}, +}) diff --git a/miniprogram/pages/date/date.json b/miniprogram/pages/date/date.json new file mode 100644 index 0000000..a97367d --- /dev/null +++ b/miniprogram/pages/date/date.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} diff --git a/miniprogram/pages/date/date.scss b/miniprogram/pages/date/date.scss new file mode 100644 index 0000000..e38e96b --- /dev/null +++ b/miniprogram/pages/date/date.scss @@ -0,0 +1,97 @@ +/* pages/date/date.wxss */ +.container { + padding: 20rpx; + background-color: #f5f5f5; + min-height: 100vh; +} + +.header { + margin-bottom: 30rpx; + text-align: center; +} + +.title { + font-size: 36rpx; + font-weight: bold; + color: #333; +} + +.section { + width: 100%; + background-color: #fff; + border-radius: 16rpx; + padding: 30rpx; + margin-bottom: 30rpx; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); +} + +.section-title { + font-size: 32rpx; + font-weight: bold; + color: #333; + margin-bottom: 10rpx; +} + +.section-desc { + font-size: 24rpx; + color: #666; + margin-bottom: 20rpx; + line-height: 1.6; +} + +.example-list { + display: flex; + flex-direction: column; + gap: 24rpx; +} + +.example-item { + padding: 20rpx; + background-color: #f9f9f9; + border-radius: 12rpx; + border-left: 4rpx solid #1890ff; +} + +.example-label { + font-size: 28rpx; + font-weight: 600; + color: #333; + margin-bottom: 8rpx; +} + +.example-format { + font-size: 24rpx; + color: #999; + margin-bottom: 12rpx; + font-family: 'Courier New', monospace; +} + +.example-value { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16rpx; +} + +.result-text { + flex: 1; + font-size: 26rpx; + color: #1890ff; + word-break: break-all; + font-family: 'Courier New', monospace; +} + +.copy-btn { + padding: 8rpx 20rpx; + background-color: #1890ff; + color: #fff; + font-size: 24rpx; + border-radius: 8rpx; + white-space: nowrap; + flex-shrink: 0; +} + +.copy-btn:active { + background-color: #40a9ff; + opacity: 0.8; +} diff --git a/miniprogram/pages/date/date.ts b/miniprogram/pages/date/date.ts new file mode 100644 index 0000000..5dff8b9 --- /dev/null +++ b/miniprogram/pages/date/date.ts @@ -0,0 +1,204 @@ +// pages/date/date.ts +import { formatTime, formatDateTime } from '../../utils/util' + +Page({ + /** + * 页面的初始数据 + */ + data: { + // formatTime 示例 + formatTimeExamples: [] as Array<{ label: string; value: string }>, + // formatDateTime 示例 - 不同格式模板 + formatDateTimeExamples: [] as Array<{ label: string; format: string; value: string }>, + // formatDateTime 示例 - 不同输入类型 + inputTypeExamples: [] as Array<{ label: string; input: string; value: string }>, + }, + + /** + * 生命周期函数--监听页面加载 + */ + onLoad() { + this.initExamples() + }, + + /** + * 初始化所有示例数据 + */ + initExamples() { + const now = new Date() + const timestamp = now.getTime() + const dateString = '2024-01-15 14:30:45' + const chineseDateString = '2024年1月15日' + + // formatTime 示例 + const formatTimeExamples = [ + { + label: '当前时间', + value: formatTime(now), + }, + { + label: '指定日期', + value: formatTime(new Date(2024, 0, 15, 14, 30, 45)), + }, + { + label: '昨天', + value: formatTime(new Date(timestamp - 24 * 60 * 60 * 1000)), + }, + { + label: '明天', + value: formatTime(new Date(timestamp + 24 * 60 * 60 * 1000)), + }, + ] + + // formatDateTime 示例 - 不同格式模板 + const formatDateTimeExamples = [ + { + label: '默认格式(YYYY/MM/DD HH:mm:ss)', + format: 'YYYY/MM/DD HH:mm:ss', + value: formatDateTime(now), + }, + { + label: '中文日期格式', + format: 'YYYY年MM月DD日 HH:mm:ss', + value: formatDateTime(now, 'YYYY年MM月DD日 HH:mm:ss'), + }, + { + label: '短日期格式', + format: 'YY/MM/DD', + value: formatDateTime(now, 'YY/MM/DD'), + }, + { + label: '12小时制格式', + format: 'YYYY-MM-DD hh:mm:ss A', + value: formatDateTime(now, 'YYYY-MM-DD hh:mm:ss A'), + }, + { + label: '24小时制格式', + format: 'YYYY-MM-DD HH:mm:ss', + value: formatDateTime(now, 'YYYY-MM-DD HH:mm:ss'), + }, + { + label: '仅日期', + format: 'YYYY-MM-DD', + value: formatDateTime(now, 'YYYY-MM-DD'), + }, + { + label: '仅时间', + format: 'HH:mm:ss', + value: formatDateTime(now, 'HH:mm:ss'), + }, + { + label: '带毫秒', + format: 'YYYY-MM-DD HH:mm:ss.SSS', + value: formatDateTime(now, 'YYYY-MM-DD HH:mm:ss.SSS'), + }, + { + label: '美式日期格式', + format: 'MM/DD/YYYY', + value: formatDateTime(now, 'MM/DD/YYYY'), + }, + { + label: '完整中文格式', + format: 'YYYY年MM月DD日 HH时mm分ss秒', + value: formatDateTime(now, 'YYYY年MM月DD日 HH时mm分ss秒'), + }, + ] + + // formatDateTime 示例 - 不同输入类型 + const inputTypeExamples = [ + { + label: 'Date 对象', + input: 'new Date()', + value: formatDateTime(now), + }, + { + label: '时间戳(数字)', + input: `${timestamp}`, + value: formatDateTime(timestamp), + }, + { + label: '日期字符串(标准格式)', + input: dateString, + value: formatDateTime(dateString), + }, + { + label: '中文日期字符串', + input: chineseDateString, + value: formatDateTime(chineseDateString), + }, + { + label: '仅年份字符串', + input: '2024', + value: formatDateTime('2024'), + }, + { + label: '年月字符串', + input: '2024/01', + value: formatDateTime('2024/01'), + }, + ] + + this.setData({ + formatTimeExamples, + formatDateTimeExamples, + inputTypeExamples, + }) + }, + + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady() {}, + + /** + * 生命周期函数--监听页面显示 + */ + onShow() {}, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide() {}, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload() {}, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh() {}, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom() {}, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage() {}, + + /** + * 复制文本到剪贴板 + */ + copyText(e: WechatMiniprogram.Touch) { + const { text } = e.currentTarget.dataset + if (!text) { + return + } + + wx.setClipboardData({ + data: text, + success: () => { + wx.showToast({ + title: '复制成功', + icon: 'success', + }).catch(() => { + console.error('showToast error') + }) + }, + }) + }, +}) diff --git a/miniprogram/pages/date/date.wxml b/miniprogram/pages/date/date.wxml new file mode 100644 index 0000000..8d81ef5 --- /dev/null +++ b/miniprogram/pages/date/date.wxml @@ -0,0 +1,74 @@ + + + + 日期时间格式化示例 + + + + + formatTime 函数示例 + 固定格式:YYYY/MM/DD HH:mm:ss + + + {{item.label}}: + + {{item.value}} + + 复制 + + + + + + + + + formatDateTime 函数示例 - 不同格式模板 + 支持自定义格式模板,灵活配置日期时间显示格式 + + + {{item.label}} + 格式模板:{{item.format}} + + {{item.value}} + + 复制 + + + + + + + + + formatDateTime 函数示例 - 不同输入类型 + 支持 Date 对象、时间戳、日期字符串等多种输入类型 + + + {{item.label}} + 输入:{{item.input}} + + {{item.value}} + + 复制 + + + + + + \ No newline at end of file diff --git a/miniprogram/pages/index/index.json b/miniprogram/pages/index/index.json new file mode 100644 index 0000000..a97367d --- /dev/null +++ b/miniprogram/pages/index/index.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} diff --git a/miniprogram/pages/index/index.scss b/miniprogram/pages/index/index.scss new file mode 100644 index 0000000..19cc86f --- /dev/null +++ b/miniprogram/pages/index/index.scss @@ -0,0 +1,133 @@ +/* pages/index.wxss */ +.container { + padding: 40rpx; + background-color: #f5f5f5; + min-height: 100vh; +} + +.header { + text-align: center; + margin-bottom: 40rpx; +} + +.title { + font-size: 36rpx; + font-weight: bold; + color: #333; +} + +.section { + width: 100%; + background-color: #fff; + border-radius: 16rpx; + padding: 30rpx; + margin-bottom: 30rpx; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); +} + +.section-title { + font-size: 32rpx; + font-weight: 600; + color: #333; + margin-bottom: 24rpx; + padding-bottom: 16rpx; + border-bottom: 2rpx solid #eee; +} + +.input-textarea { + width: 100%; + min-height: 200rpx; + padding: 20rpx; + border: 2rpx solid #e0e0e0; + border-radius: 8rpx; + font-size: 28rpx; + background-color: #fafafa; + box-sizing: border-box; +} + +.action-bar { + display: flex; + gap: 20rpx; + margin-bottom: 24rpx; +} + +.btn { + flex: 1; + border-radius: 8rpx; + font-size: 28rpx; + border: none; +} + +.btn-primary { + background-color: #07c160; + color: #fff; +} + +.btn-primary:active { + background-color: #06ad56; +} + +.btn-secondary { + background-color: #576b95; + color: #fff; +} + +.btn-secondary:active { + background-color: #4a5d84; +} + +.result-box { + margin-bottom: 24rpx; + padding: 20rpx; + background-color: #fafafa; + border-radius: 8rpx; + border: 2rpx solid #e0e0e0; +} + +.result-label { + font-size: 24rpx; + color: #666; + margin-bottom: 12rpx; +} + +.result-content { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16rpx; +} + +.result-text { + flex: 1; + font-size: 26rpx; + color: #333; + word-break: break-all; + line-height: 1.6; +} + +.copy-btn { + padding: 8rpx 20rpx; + background-color: #07c160; + color: #fff; + font-size: 24rpx; + border-radius: 6rpx; + white-space: nowrap; +} + +.copy-btn:active { + background-color: #06ad56; +} + +.error-box { + margin-top: 30rpx; + padding: 20rpx; + background-color: #fff3cd; + border: 2rpx solid #ffc107; + border-radius: 8rpx; +} + +.error-text { + font-size: 26rpx; + color: #856404; + line-height: 1.6; +} diff --git a/miniprogram/pages/index/index.ts b/miniprogram/pages/index/index.ts new file mode 100644 index 0000000..9c267e5 --- /dev/null +++ b/miniprogram/pages/index/index.ts @@ -0,0 +1,301 @@ +// pages/index.ts +import { Encrypt, Decrypt, BASE64Encrypt, BASE64Decrypt, AESError } from '../../utils/aes' + +Page({ + /** + * 页面的初始数据 + */ + data: { + // 输入原文 + plainText: 'Hello, 小程序 AES 加密示例!', + // 标准 Base64 加密结果 + encryptedText: '', + // 标准 Base64 解密结果 + decryptedText: '', + // URL Safe Base64 加密结果 + base64EncryptedText: '', + // URL Safe Base64 解密结果 + base64DecryptedText: '', + // 错误信息 + errorMessage: '', + }, + + /** + * 生命周期函数--监听页面加载 + */ + onLoad() { + // 页面加载时自动执行一次加密解密示例 + this.handleEncrypt() + }, + + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady() {}, + + /** + * 生命周期函数--监听页面显示 + */ + onShow() {}, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide() {}, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload() {}, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh() {}, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom() {}, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage() {}, + + /** + * 输入原文变化 + */ + onPlainTextInput(e: WechatMiniprogram.Input) { + this.setData({ + plainText: e.detail.value, + errorMessage: '', + }) + }, + + /** + * 执行标准 Base64 加密 + */ + handleEncrypt() { + try { + const { plainText } = this.data + if (!plainText.trim()) { + wx.showToast({ + title: '请输入要加密的内容', + icon: 'none', + }).catch(() => { + console.error('showToast error') + }) + return + } + + // 执行加密 + const encrypted = Encrypt(plainText) + this.setData({ + encryptedText: encrypted, + errorMessage: '', + }) + + wx.showToast({ + title: '加密成功', + icon: 'success', + }).catch(() => { + console.error('showToast error') + }) + } catch (error) { + const errorMsg = error instanceof AESError ? error.message : '加密失败' + this.setData({ + errorMessage: errorMsg, + }) + wx.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000, + }).catch(() => { + console.error('showToast error') + }) + } + }, + + /** + * 执行标准 Base64 解密 + */ + handleDecrypt() { + try { + const { encryptedText } = this.data + if (!encryptedText.trim()) { + wx.showToast({ + title: '请输入要解密的内容', + icon: 'none', + }).catch(() => { + console.error('showToast error') + }) + return + } + + // 执行解密 + const decrypted = Decrypt(encryptedText) + if (decrypted === null) { + this.setData({ + errorMessage: '解密失败:密钥不匹配或密文被篡改', + }) + wx.showToast({ + title: '解密失败', + icon: 'none', + duration: 2000, + }).catch(() => { + console.error('showToast error') + }) + return + } + + this.setData({ + decryptedText: decrypted, + errorMessage: '', + }) + + wx.showToast({ + title: '解密成功', + icon: 'success', + }).catch(() => { + console.error('showToast error') + }) + } catch (error) { + const errorMsg = error instanceof AESError ? error.message : '解密失败' + this.setData({ + errorMessage: errorMsg, + }) + wx.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000, + }).catch(() => { + console.error('showToast error') + }) + } + }, + + /** + * 执行 URL Safe Base64 加密 + */ + handleBase64Encrypt() { + try { + const { plainText } = this.data + if (!plainText.trim()) { + wx.showToast({ + title: '请输入要加密的内容', + icon: 'none', + }).catch(() => { + console.error('showToast error') + }) + return + } + + // 执行 URL Safe Base64 加密 + const encrypted = BASE64Encrypt(plainText) + this.setData({ + base64EncryptedText: encrypted, + errorMessage: '', + }) + + wx.showToast({ + title: '加密成功', + icon: 'success', + }).catch(() => { + console.error('showToast error') + }) + } catch (error) { + const errorMsg = error instanceof AESError ? error.message : '加密失败' + this.setData({ + errorMessage: errorMsg, + }) + wx.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000, + }).catch(() => { + console.error('showToast error') + }) + } + }, + + /** + * 执行 URL Safe Base64 解密 + */ + handleBase64Decrypt() { + try { + const { base64EncryptedText } = this.data + if (!base64EncryptedText.trim()) { + wx.showToast({ + title: '请输入要解密的内容', + icon: 'none', + }).catch(() => { + console.error('showToast error') + }) + return + } + + // 执行 URL Safe Base64 解密 + const decrypted = BASE64Decrypt(base64EncryptedText) + if (decrypted === null) { + this.setData({ + errorMessage: '解密失败:密钥不匹配或密文被篡改', + }) + wx.showToast({ + title: '解密失败', + icon: 'none', + duration: 2000, + }).catch(() => { + console.error('showToast error') + }) + return + } + + this.setData({ + base64DecryptedText: decrypted, + errorMessage: '', + }) + + wx.showToast({ + title: '解密成功', + icon: 'success', + }).catch(() => { + console.error('showToast error') + }) + } catch (error) { + const errorMsg = error instanceof AESError ? error.message : '解密失败' + this.setData({ + errorMessage: errorMsg, + }) + wx.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000, + }).catch(() => { + console.error('showToast error') + }) + } + }, + + /** + * 复制文本到剪贴板 + */ + copyText(e: WechatMiniprogram.Touch) { + const { text } = e.currentTarget.dataset + if (!text) { + return + } + + wx.setClipboardData({ + data: text, + success: () => { + wx.showToast({ + title: '复制成功', + icon: 'success', + }).catch(() => { + console.error('showToast error') + }) + }, + }) + }, +}) diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml new file mode 100644 index 0000000..32e2135 --- /dev/null +++ b/miniprogram/pages/index/index.wxml @@ -0,0 +1,104 @@ + + + + AES 加密解密示例 + + + + + 原文输入 +