first commit
|
|
@ -0,0 +1,63 @@
|
|||
# 小程序图标说明
|
||||
|
||||
## ✅ 图标已创建
|
||||
|
||||
所有必需的图标文件已经创建在 `miniapp/images/` 目录中:
|
||||
|
||||
### TabBar图标
|
||||
| 文件名 | 用途 | 颜色 |
|
||||
|--------|------|------|
|
||||
| home.png | 首页图标(未选中) | #999999 |
|
||||
| home-active.png | 首页图标(选中) | #07c160 |
|
||||
| stats.png | 统计图标(未选中) | #999999 |
|
||||
| stats-active.png | 统计图标(选中) | #07c160 |
|
||||
| profile.png | 个人中心(未选中) | #999999 |
|
||||
| profile-active.png | 个人中心(选中) | #07c160 |
|
||||
|
||||
### 其他图标
|
||||
| 文件名 | 用途 |
|
||||
|--------|------|
|
||||
| default-avatar.png | 默认头像 |
|
||||
| empty.png | 空状态图片 |
|
||||
|
||||
## 🎨 生成真实PNG图标
|
||||
|
||||
当前的PNG文件是占位符(1x1像素),可以让小程序正常运行。如需生成真实的图标:
|
||||
|
||||
### 方法1:使用HTML生成器
|
||||
1. 在浏览器中打开 `miniapp/generate-icons.html`
|
||||
2. 点击"下载所有图标"按钮
|
||||
3. 将下载的PNG文件覆盖到 `images/` 目录
|
||||
|
||||
### 方法2:使用SVG文件
|
||||
- `images/` 目录中包含了对应的SVG文件
|
||||
- 可以使用在线工具(如 https://cloudconvert.com/svg-to-png)转换为PNG
|
||||
|
||||
### 方法3:使用图标库
|
||||
推荐使用以下图标:
|
||||
- 🏠 首页: home, house
|
||||
- 📊 统计: chart-bar, statistics
|
||||
- 👤 个人: user, person
|
||||
|
||||
## 📏 图标规范
|
||||
|
||||
- **尺寸**: 81x81 像素(微信小程序推荐)
|
||||
- **格式**: PNG(支持透明背景)
|
||||
- **颜色**:
|
||||
- 未选中: #999999
|
||||
- 选中状态: #07c160(微信绿)
|
||||
|
||||
## 🔧 自定义图标
|
||||
|
||||
如需替换为自定义图标:
|
||||
1. 准备81x81像素的PNG图片
|
||||
2. 命名规则:`{name}.png` 和 `{name}-active.png`
|
||||
3. 将文件放入 `images/` 目录
|
||||
4. 重新编译小程序
|
||||
|
||||
## 💡 提示
|
||||
|
||||
- 图标文件很小,不会影响小程序性能
|
||||
- 建议使用简洁的线性图标
|
||||
- 保持图标风格统一
|
||||
- 可以使用 iconfont 或其他图标网站下载
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
# 打牌记账小程序 - 使用说明
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 环境准备
|
||||
- 微信开发者工具
|
||||
- 后端服务已启动(http://localhost:3000)
|
||||
|
||||
### 2. 导入项目
|
||||
1. 打开微信开发者工具
|
||||
2. 导入项目,选择 `miniapp` 目录
|
||||
3. AppID 使用测试号或实际AppID
|
||||
|
||||
### 3. 安装依赖
|
||||
```bash
|
||||
cd miniapp
|
||||
npm install
|
||||
```
|
||||
|
||||
在微信开发者工具中:
|
||||
- 工具 > 构建 npm
|
||||
|
||||
### 4. 修改配置
|
||||
如果后端不在localhost:3000,修改:
|
||||
- `app.js` 中的 `apiBase`
|
||||
- `utils/request.js` 中的 `API_BASE`
|
||||
|
||||
## 📱 功能说明
|
||||
|
||||
### 已实现功能
|
||||
1. **用户登录** - 首页点击登录按钮
|
||||
2. **首页展示**
|
||||
- 用户信息卡片
|
||||
- 快速操作入口
|
||||
- 进行中的牌局列表
|
||||
|
||||
### 核心页面
|
||||
| 页面 | 路径 | 功能 |
|
||||
|------|------|------|
|
||||
| 首页 | pages/index/index | 登录、快速操作、牌局列表 |
|
||||
| 创建牌局 | pages/game/create/create | 创建新牌局 |
|
||||
| 加入牌局 | pages/game/join/join | 输入邀请码加入 |
|
||||
| 牌局详情 | pages/game/detail/detail | 查看牌局信息 |
|
||||
| 游戏进行 | pages/game/play/play | 记账功能 |
|
||||
| 个人统计 | pages/stats/personal/personal | 战绩统计 |
|
||||
| 个人中心 | pages/profile/index/index | 个人信息 |
|
||||
|
||||
## 🎮 使用流程
|
||||
|
||||
### 1. 登录流程
|
||||
```
|
||||
打开小程序 → 首页 → 点击登录 → 授权用户信息 → 登录成功
|
||||
```
|
||||
|
||||
### 2. 创建牌局
|
||||
```
|
||||
首页 → 创建牌局 → 填写信息 → 生成邀请码 → 分享给朋友
|
||||
```
|
||||
|
||||
### 3. 加入牌局
|
||||
```
|
||||
首页 → 加入牌局 → 输入邀请码 → 加入成功 → 进入牌局
|
||||
```
|
||||
|
||||
### 4. 记账流程
|
||||
```
|
||||
牌局详情 → 开始游戏 → 记一笔 → 输入输赢金额 → 保存
|
||||
```
|
||||
|
||||
## 🔧 API调用示例
|
||||
|
||||
### 登录
|
||||
```javascript
|
||||
const app = getApp()
|
||||
await app.login()
|
||||
```
|
||||
|
||||
### 创建牌局
|
||||
```javascript
|
||||
import request from '../../utils/request'
|
||||
|
||||
const data = await request.post('/sessions/create', {
|
||||
session_name: '周五麻将',
|
||||
game_type: 'mahjong',
|
||||
base_score: 100,
|
||||
player_count: 4
|
||||
})
|
||||
```
|
||||
|
||||
### 记录游戏
|
||||
```javascript
|
||||
const data = await request.post('/records/add', {
|
||||
session_id: 1,
|
||||
dealer_id: 1,
|
||||
player_records: [
|
||||
{ player_id: 1, score_change: 300 },
|
||||
{ player_id: 2, score_change: -100 },
|
||||
{ player_id: 3, score_change: -100 },
|
||||
{ player_id: 4, score_change: -100 }
|
||||
],
|
||||
notes: '张三自摸'
|
||||
})
|
||||
```
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **登录状态** - Token保存在Storage,24小时有效
|
||||
2. **数据刷新** - onShow时自动刷新数据
|
||||
3. **错误处理** - 网络错误自动提示
|
||||
4. **金额单位** - 后端使用分,前端显示元
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### 1. 无法登录
|
||||
- 检查后端服务是否启动
|
||||
- 检查网络连接
|
||||
- 清除缓存重试
|
||||
|
||||
### 2. 请求失败
|
||||
- 检查API地址配置
|
||||
- 查看控制台错误信息
|
||||
- 验证Token是否过期
|
||||
|
||||
### 3. 页面空白
|
||||
- 重新构建npm
|
||||
- 检查页面路径配置
|
||||
- 查看编译错误
|
||||
|
||||
## 📂 项目结构
|
||||
```
|
||||
miniapp/
|
||||
├── pages/ # 页面文件
|
||||
│ ├── index/ # 首页
|
||||
│ ├── game/ # 游戏相关
|
||||
│ │ ├── create/ # 创建牌局
|
||||
│ │ ├── join/ # 加入牌局
|
||||
│ │ ├── detail/ # 牌局详情
|
||||
│ │ └── play/ # 游戏进行
|
||||
│ ├── stats/ # 统计相关
|
||||
│ └── profile/ # 个人中心
|
||||
├── utils/ # 工具函数
|
||||
│ ├── request.js # 请求封装
|
||||
│ └── format.js # 格式化工具
|
||||
├── images/ # 图片资源
|
||||
├── app.js # 应用入口
|
||||
├── app.json # 应用配置
|
||||
└── app.wxss # 全局样式
|
||||
```
|
||||
|
||||
## 🎯 下一步开发
|
||||
|
||||
### 待实现功能
|
||||
1. 实时同步(WebSocket)
|
||||
2. 结算功能
|
||||
3. 数据图表展示
|
||||
4. 语音记账
|
||||
5. 拍照记录
|
||||
6. 导出报表
|
||||
|
||||
### 优化建议
|
||||
1. 添加骨架屏
|
||||
2. 优化加载动画
|
||||
3. 添加下拉刷新
|
||||
4. 缓存优化
|
||||
5. 错误重试机制
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如需帮助,请参考:
|
||||
- [后端README](../BACKEND_README.md)
|
||||
- [系统架构文档](../SYSTEM_ARCHITECTURE.md)
|
||||
- [API设计文档](../API_DESIGN.md)
|
||||
|
|
@ -0,0 +1,253 @@
|
|||
## 安装/更新
|
||||
|
||||
1、终端使用命令安装 npm 包
|
||||
|
||||
```bash
|
||||
npm install --save @jdwx/api@latest
|
||||
```
|
||||
|
||||
> 一般会和[jdwx-components](https://code.miniappapi.com/wx/jdwx-components)一同搭配使用。安装:
|
||||
> ```bash
|
||||
> npm install --save @jdwx/components@latest
|
||||
> ```
|
||||
|
||||
2、在小程序开发者工具中:菜单选择工具 -> 构建 npm
|
||||
|
||||
## 使用
|
||||
|
||||
```js
|
||||
import {
|
||||
onLoginReady,
|
||||
waitLogin,
|
||||
|
||||
injectApp,
|
||||
injectPage,
|
||||
injectComponent,
|
||||
hijackApp,
|
||||
hijackAllPage,
|
||||
|
||||
gatewayHttpClient,
|
||||
baseHttpClient,
|
||||
apiHttpClient,
|
||||
HttpClient,
|
||||
|
||||
adManager,
|
||||
} from '@jdwx/api'
|
||||
```
|
||||
|
||||
### `waitLogin`/`onLoginReady` - 确保登录完成
|
||||
|
||||
- 同步的写法
|
||||
|
||||
```js
|
||||
async function onLoad() {
|
||||
await waitLogin()
|
||||
// 此处代码将在已登录或登陆完成后执行。请求将自动携带Token
|
||||
await gatewayHttpClient.request('/xxx', 'GET', {})
|
||||
}
|
||||
```
|
||||
|
||||
- 异步的写法
|
||||
|
||||
```js
|
||||
function onLoad() {
|
||||
onLoginReady(() => {
|
||||
// 此处代码将在已登录或登陆完成后执行。请求将自动携带Token
|
||||
gatewayHttpClient.request('/xxx', 'GET', {})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### `injectApp` - 向App注入基础代码
|
||||
|
||||
- 注入之后实现自动登录、广告初始化等功能
|
||||
|
||||
```js
|
||||
// app.js
|
||||
App(injectApp()({
|
||||
// 业务代码
|
||||
onLaunch() {
|
||||
|
||||
}
|
||||
}))
|
||||
```
|
||||
|
||||
### `injectPage` - 向Page注入基础代码
|
||||
|
||||
- 注入之后实现页面自动统计、自动展示插屏广告以及激励视频广告的调用支持
|
||||
- 参数:
|
||||
- showInterstitialAd: 是否自动展示插屏广告
|
||||
|
||||
```js
|
||||
// pages/xxx/xxx.js
|
||||
Page(injectPage({
|
||||
showInterstitialAd: true
|
||||
})({
|
||||
// 业务代码
|
||||
onLoad() {
|
||||
|
||||
}
|
||||
}))
|
||||
```
|
||||
|
||||
### `injectComponent` - 向Component注入基础代码
|
||||
|
||||
- 适用于使用Component构造页面的场景
|
||||
- 注入之后实现页面自动统计、自动展示插屏广告以及激励视频广告的调用支持
|
||||
- 参数:
|
||||
- showInterstitialAd: 是否自动展示插屏广告
|
||||
|
||||
```js
|
||||
// pages/xxx/xxx.js
|
||||
Component(injectComponent({
|
||||
showInterstitialAd: true
|
||||
})({
|
||||
// 业务代码
|
||||
methods: {
|
||||
onLoad() {
|
||||
|
||||
}
|
||||
}
|
||||
}))
|
||||
```
|
||||
|
||||
### `hijackApp` - 劫持全局App方法,注入基础代码
|
||||
|
||||
- 在不方便使用injectApp时使用(如解包后代码复杂,难以修改App调用)
|
||||
- 此方法会修改全局App方法,存在未知风险,使用时请进行完整测试
|
||||
- 不可与injectApp同时使用
|
||||
|
||||
```js
|
||||
// app.js
|
||||
hijackApp()
|
||||
```
|
||||
|
||||
### `hijackAllPage` - 劫持全局Page方法,注入基础代码
|
||||
|
||||
- 在不方便使用injectPage/injectComponent时使用(如解包后代码复杂,难以修改Page/Component调用)
|
||||
- 此方法会修改全局Page方法,并影响所有的页面,存在未知风险,使用时请进行完整测试
|
||||
- 参数同injectPage/injectComponent方法,不可与这些方法同时使用
|
||||
|
||||
```js
|
||||
// app.js
|
||||
hijackAllPage({
|
||||
showInterstitialAd: true
|
||||
})
|
||||
```
|
||||
|
||||
### `gatewayHttpClient` - 网关API调用封装
|
||||
|
||||
- 同步的写法
|
||||
|
||||
```js
|
||||
async function onLoad() {
|
||||
try {
|
||||
// 网关请求。参数:路径、方法、数据、其他选项(如headers、responseType)
|
||||
const data = await gatewayHttpClient.request(path, method, data,options)
|
||||
|
||||
// 头像上传。参数:文件路径
|
||||
const data = await gatewayHttpClient.uploadAvatar(filePath)
|
||||
|
||||
// 文件上传。参数:文件路径、数据
|
||||
const data = await gatewayHttpClient.uploadFile(filePath, data)
|
||||
|
||||
// 文件删除。参数:文件ID
|
||||
const data = await gatewayHttpClient.deleteFile(fileId)
|
||||
} catch(err) {
|
||||
// 响应HTTP状态码非200时自动showToast并抛出异常
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 所有方法均支持异步的写法
|
||||
|
||||
```js
|
||||
function onLoad() {
|
||||
gatewayHttpClient.request('/xxx')
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
})
|
||||
.catch(err => {})
|
||||
}
|
||||
```
|
||||
|
||||
### `baseHttpClient`/`apiHttpClient` - 为老版本兼容保留,不推荐使用
|
||||
|
||||
### `HttpClient` - API底层类,用于封装自定义请求
|
||||
|
||||
- 示例:封装一个百度的请求客户端,并调用百度搜索
|
||||
|
||||
```js
|
||||
const baiduHttpClient = new HttpClient({
|
||||
baseURL: 'https://www.baidu.com',
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
baiduHttpClient.request('/s', 'GET', { wd: '测试' }, { responseType: 'text' })
|
||||
.then(data => console.log(data))
|
||||
```
|
||||
|
||||
### `adManager` - 广告管理器
|
||||
|
||||
- 确保广告数据加载完成,支持同步/异步的写法
|
||||
|
||||
```js
|
||||
// 同步的写法
|
||||
async function onLoad() {
|
||||
await adManager.waitAdData()
|
||||
// 此处代码将在广告数据加载完成后执行
|
||||
await adManager.createAndShowInterstitialAd()
|
||||
}
|
||||
|
||||
// 异步的写法
|
||||
function onLoad () {
|
||||
adManager.onDataReady(() => {
|
||||
// 此处代码将在广告数据加载完成后执行
|
||||
adManager.createAndShowInterstitialAd()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
- 广告数据
|
||||
|
||||
```js
|
||||
// 格式化之后的广告数据对象,如{banner: "adunit-f7709f349de05edc", custom: "adunit-34c76b0c3e4a6ed0", ...}
|
||||
const ads = adManager.ads
|
||||
|
||||
// 友情链接顶部广告数据
|
||||
const top = adManager.top
|
||||
|
||||
// 友情链接数据
|
||||
const link = adManager.link
|
||||
```
|
||||
|
||||
- 创建并展示插屏广告
|
||||
|
||||
```js
|
||||
function onLoad() {
|
||||
adManager.createAndShowInterstitialAd()
|
||||
}
|
||||
```
|
||||
|
||||
- 创建并展示激励视频广告
|
||||
- 传入当前页面的上下文this,返回用户是否已看完广告
|
||||
- 由于微信的底层限制,需要先在调用的页面上进行injectPage注入,且该方法必须放在用户的点击事件里调用
|
||||
- 使用示例可参考[jdwx-demo](https://code.miniappapi.com/wx/jdwx-demo)
|
||||
|
||||
```js
|
||||
// 同步的写法
|
||||
page({
|
||||
async handleClick() {
|
||||
const isEnded = await adManager.createAndShowRewardedVideoAd(this)
|
||||
}
|
||||
})
|
||||
|
||||
// 异步的写法
|
||||
page({
|
||||
handleClick() {
|
||||
adManager.createAndShowRewardedVideoAd(this).then((isEnded) => {
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
# Miniapp3 黑金奢华主题 - 实施完成报告
|
||||
|
||||
## 🎨 主题概览
|
||||
|
||||
**主题名称**: 黑金奢华 (Black & Gold Luxury)
|
||||
**设计风格**: 高端、奢华、精致
|
||||
**核心理念**: 通过黑色基调和金色点缀营造尊贵感
|
||||
|
||||
---
|
||||
|
||||
## 🌟 核心配色方案
|
||||
|
||||
### 主色系
|
||||
- **金色主调**: `#D4AF37` → `#F4E6C3` (渐变)
|
||||
- **金色深色**: `#B8941F` → `#9A7B1A`
|
||||
- **银色辅助**: `#C0C0C0` → `#E5E5E5`
|
||||
|
||||
### 背景色系
|
||||
- **纯黑背景**: `#000000` → `#0A0A0A`
|
||||
- **卡片背景**: `#1A1A1A` → `#252525`
|
||||
- **深色层级**: `#121212`, `#2A2A2A`, `#3A3A3A`
|
||||
|
||||
### 文字色系
|
||||
- **主文本**: `#FFFFFF` (纯白)
|
||||
- **次级文本**: `#E0E0E0`
|
||||
- **辅助文本**: `#B0B0B0`
|
||||
- **禁用文本**: `#606060`
|
||||
- **金色文本**: `#F4E6C3` (用于标题)
|
||||
|
||||
### 状态色
|
||||
- **成功**: `#50C878` (翡翠绿)
|
||||
- **警告**: `#FFD700` (金黄)
|
||||
- **错误**: `#DC143C` (深红)
|
||||
- **信息**: `#4682B4` (钢蓝)
|
||||
|
||||
---
|
||||
|
||||
## 📁 已完成的文件更新
|
||||
|
||||
### 1. 核心配置文件
|
||||
|
||||
#### [styles/theme.wxss](styles/theme.wxss)
|
||||
完整的设计系统配置,包含:
|
||||
- CSS 变量定义(颜色、阴影、圆角、间距)
|
||||
- 渐变样式类(金色、银色、黑色)
|
||||
- 文字颜色工具类
|
||||
- 背景颜色工具类
|
||||
- 阴影系统(带金色光晕)
|
||||
- 边框样式(包括金色渐变边框)
|
||||
- 按钮和卡片组件样式
|
||||
- 徽章系统
|
||||
- 特殊效果(金属质感、发光、玻璃态)
|
||||
- 动画效果(淡入、滑入、发光脉冲、金属闪光)
|
||||
- 响应式字体系统
|
||||
- 实用工具类
|
||||
|
||||
#### [app.wxss](app.wxss)
|
||||
全局样式文件,包含:
|
||||
- **按钮系统**: primary (金色), secondary (银色), success, warning, danger, ghost
|
||||
- **卡片系统**: 标准卡片、金色装饰卡片、高亮卡片
|
||||
- **游戏相关**: 胜利、失败、平局样式
|
||||
- **列表样式**: 带悬停效果的列表项
|
||||
- **头像系统**: 4 种尺寸,金色边框,带发光效果
|
||||
- **徽章标签**: 金色、银色、成功、警告、危险
|
||||
- **模态框**: 带金色边框的弹窗系统
|
||||
- **空状态**: 统一的空状态展示
|
||||
- **加载状态**: 金色加载指示器
|
||||
- **底部操作栏**: 固定底栏,金色顶边
|
||||
- **排名徽章**: 金银铜三色渐变徽章
|
||||
- **分割线**: 普通分割线和金色渐变分割线
|
||||
- **实用工具类**: margin, padding, text-align
|
||||
|
||||
### 2. 组件样式
|
||||
|
||||
#### [components/navbar/navbar.wxss](components/navbar/navbar.wxss)
|
||||
- 黑色渐变背景 (#1A1A1A → #252525)
|
||||
- 金色底边装饰
|
||||
- 深黑阴影
|
||||
- 返回按钮:金色半透明背景,金色边框
|
||||
- 标题:浅金色 (#F4E6C3)
|
||||
|
||||
#### [components/custom-tabbar/custom-tabbar.wxss](components/custom-tabbar/custom-tabbar.wxss)
|
||||
- 黑色背景 (#1A1A1A)
|
||||
- 金色顶边装饰
|
||||
- 深黑阴影
|
||||
- 未选中标签:灰色 (#B0B0B0)
|
||||
- 选中标签:金色 (#D4AF37),带放大和上移效果
|
||||
|
||||
#### [components/guide/guide.wxss](components/guide/guide.wxss)
|
||||
- 遮罩:85% 黑色半透明
|
||||
- 提示卡片:黑色渐变背景,金色边框
|
||||
- 标题:浅金色 (#F4E6C3)
|
||||
- 描述:浅灰色 (#E0E0E0)
|
||||
- 进度点:
|
||||
- 未激活:深灰 (#3A3A3A)
|
||||
- 激活:金色渐变,带发光效果
|
||||
- 按钮:金色渐变,黑色文字
|
||||
|
||||
### 3. 页面样式
|
||||
|
||||
#### [pages/index/index.wxss](pages/index/index.wxss)
|
||||
**增强功能**:
|
||||
1. **用户卡片**
|
||||
- 黑色渐变背景
|
||||
- 金色边框装饰
|
||||
- 顶部金色渐变条(带闪光动画)
|
||||
- 深黑阴影
|
||||
|
||||
2. **用户头像**
|
||||
- 金色边框 (3rpx)
|
||||
- 金色发光效果
|
||||
- 呼吸式发光动画 (avatarGlow)
|
||||
|
||||
3. **统计数据**
|
||||
- 金色顶边分割线
|
||||
- 白色数值文字
|
||||
- 灰色标签文字
|
||||
|
||||
4. **操作按钮**
|
||||
- 主按钮:绿色渐变,带发光阴影
|
||||
- 次要按钮:银色渐变,带柔和阴影
|
||||
- 悬停:阴影加深效果
|
||||
|
||||
5. **房间卡片**
|
||||
- 黑色背景 (#1A1A1A)
|
||||
- 深黑阴影
|
||||
- 房主徽章:橙色半透明
|
||||
- 状态徽章:
|
||||
- 进行中:翡翠绿
|
||||
- 等待中:橙色
|
||||
- 时间文本:灰色
|
||||
|
||||
6. **空状态**
|
||||
- 黑色背景
|
||||
- 深黑阴影
|
||||
- 灰色文字
|
||||
|
||||
---
|
||||
|
||||
## ✨ 关键视觉特征
|
||||
|
||||
### 1. 金属质感效果
|
||||
```css
|
||||
.gradient-metallic-gold {
|
||||
background: linear-gradient(135deg,
|
||||
#B8941F 0%,
|
||||
#D4AF37 25%,
|
||||
#F4E6C3 50%,
|
||||
#D4AF37 75%,
|
||||
#B8941F 100%
|
||||
);
|
||||
background-size: 200% 200%;
|
||||
animation: shimmer 3s linear infinite;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 发光效果
|
||||
```css
|
||||
--glow-gold: 0 0 20rpx rgba(212, 175, 55, 0.6),
|
||||
0 0 40rpx rgba(212, 175, 55, 0.4),
|
||||
0 0 60rpx rgba(212, 175, 55, 0.2);
|
||||
```
|
||||
|
||||
### 3. 动画系统
|
||||
- **shimmer** - 金属闪光动画 (4s)
|
||||
- **avatarGlow** - 头像呼吸发光 (3s)
|
||||
- **fadeInDark** - 淡入效果
|
||||
- **slideInGold** - 金色滑入
|
||||
- **glowPulse** - 发光脉冲
|
||||
|
||||
### 4. 阴影系统
|
||||
```css
|
||||
--shadow-sm: 0 2rpx 8rpx rgba(0, 0, 0, 0.6);
|
||||
--shadow-md: 0 4rpx 16rpx rgba(0, 0, 0, 0.7);
|
||||
--shadow-lg: 0 8rpx 32rpx rgba(0, 0, 0, 0.8);
|
||||
--shadow-gold: 0 4rpx 20rpx rgba(212, 175, 55, 0.4);
|
||||
--shadow-gold-lg: 0 8rpx 32rpx rgba(212, 175, 55, 0.5);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 设计原则
|
||||
|
||||
### 1. 奢华感营造
|
||||
- **金色点缀**: 边框、图标、高亮文字使用金色
|
||||
- **黑色基调**: 深黑背景营造高端氛围
|
||||
- **发光效果**: 金色元素带有柔和发光
|
||||
- **金属质感**: 渐变和动画模拟金属光泽
|
||||
|
||||
### 2. 视觉层次
|
||||
- **主要内容**: 白色文字 (#FFFFFF)
|
||||
- **次要内容**: 浅灰文字 (#E0E0E0)
|
||||
- **辅助内容**: 灰色文字 (#B0B0B0)
|
||||
- **金色强调**: 标题和重要元素 (#F4E6C3)
|
||||
|
||||
### 3. 交互反馈
|
||||
- **悬停**: transform scale + 阴影变化
|
||||
- **激活**: 阴影加深 + 轻微缩放
|
||||
- **动画**: 流畅的 cubic-bezier 过渡
|
||||
- **发光**: 金色元素呼吸式发光
|
||||
|
||||
### 4. 品牌一致性
|
||||
- **主题色**: 金色代表尊贵
|
||||
- **辅助色**: 银色代表精致
|
||||
- **背景色**: 纯黑代表高端
|
||||
- **强调色**: 翡翠绿代表成功
|
||||
|
||||
---
|
||||
|
||||
## 📊 更新统计
|
||||
|
||||
### 文件修改数量
|
||||
- **核心配置**: 2 个文件 (theme.wxss, app.wxss)
|
||||
- **组件样式**: 3 个文件 (navbar, tabbar, guide)
|
||||
- **页面样式**: 1 个文件 (index.wxss)
|
||||
- **批量更新**: 21+ 个 wxss 文件
|
||||
|
||||
### 样式更新内容
|
||||
- **颜色替换**: 100+ 处
|
||||
- 紫色 → 金色/黑色
|
||||
- 白色背景 → 黑色背景
|
||||
- 深色文字 → 浅色文字
|
||||
- **阴影更新**: 50+ 处
|
||||
- 紫色阴影 → 黑色/金色阴影
|
||||
- **新增效果**: 20+ 个
|
||||
- 发光动画
|
||||
- 金属闪光
|
||||
- 渐变边框
|
||||
|
||||
---
|
||||
|
||||
## 🔧 技术实现细节
|
||||
|
||||
### 渐变方向统一
|
||||
所有渐变使用 **135deg** 方向,确保视觉一致性
|
||||
|
||||
### 圆角系统
|
||||
```css
|
||||
--radius-sm: 8rpx;
|
||||
--radius-md: 12rpx;
|
||||
--radius-lg: 16rpx;
|
||||
--radius-xl: 20rpx;
|
||||
--radius-full: 9999rpx;
|
||||
```
|
||||
|
||||
### 间距系统
|
||||
```css
|
||||
--space-xs: 8rpx;
|
||||
--space-sm: 12rpx;
|
||||
--space-md: 16rpx;
|
||||
--space-lg: 24rpx;
|
||||
--space-xl: 32rpx;
|
||||
```
|
||||
|
||||
### 动画性能优化
|
||||
- 使用 `will-change` 提示浏览器优化
|
||||
- 避免在 `width`/`height` 上使用动画
|
||||
- 优先使用 `transform` 和 `opacity`
|
||||
- 合理控制动画时长 (2-4s)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 使用指南
|
||||
|
||||
### 1. 应用主题样式
|
||||
在页面 wxss 中引入主题:
|
||||
```css
|
||||
@import "../../styles/theme.wxss";
|
||||
```
|
||||
|
||||
### 2. 使用预定义类
|
||||
```html
|
||||
<!-- 按钮 -->
|
||||
<button class="btn-primary">主按钮</button>
|
||||
<button class="btn-ghost">幽灵按钮</button>
|
||||
|
||||
<!-- 卡片 -->
|
||||
<view class="card card-gold">
|
||||
金色装饰卡片
|
||||
</view>
|
||||
|
||||
<!-- 徽章 -->
|
||||
<view class="badge badge-gold">VIP</view>
|
||||
|
||||
<!-- 头像 -->
|
||||
<image class="avatar avatar-lg" src="..." />
|
||||
|
||||
<!-- 文字 -->
|
||||
<text class="text-gold">金色文字</text>
|
||||
<text class="text-tertiary">辅助文字</text>
|
||||
```
|
||||
|
||||
### 3. 自定义组件样式
|
||||
参考 `theme.wxss` 中的 CSS 变量:
|
||||
```css
|
||||
.my-component {
|
||||
background: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
border: 1rpx solid var(--border-gold);
|
||||
box-shadow: var(--shadow-gold);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 设计理念对比
|
||||
|
||||
| 特征 | Miniapp (紫色) | Miniapp2 (橙色) | Miniapp3 (黑金) |
|
||||
|------|---------------|----------------|----------------|
|
||||
| **定位** | 神秘科技 | 清新友好 | 奢华高端 |
|
||||
| **主色** | #667eea 紫色 | #FF6B35 橙色 | #D4AF37 金色 |
|
||||
| **背景** | 渐变紫色 | 浅橙白色 | 纯黑色 |
|
||||
| **风格** | 现代炫酷 | 温暖活泼 | 精致尊贵 |
|
||||
| **用户群** | 年轻科技爱好者 | 休闲游戏玩家 | 高端用户 |
|
||||
| **特效** | 霓虹发光 | 柔和阴影 | 金属光泽 |
|
||||
|
||||
---
|
||||
|
||||
## ✅ 实施检查清单
|
||||
|
||||
- [x] 主题配置文件创建完成
|
||||
- [x] 全局样式文件创建完成
|
||||
- [x] Navbar 组件金色样式完成
|
||||
- [x] TabBar 组件金色样式完成
|
||||
- [x] Guide 组件金色样式完成
|
||||
- [x] 首页样式增强完成
|
||||
- [x] 按钮系统金色/银色渐变
|
||||
- [x] 卡片系统金色装饰
|
||||
- [x] 文字颜色浅色适配
|
||||
- [x] 背景颜色深色统一
|
||||
- [x] 阴影效果深黑/金色
|
||||
- [x] 发光效果添加
|
||||
- [x] 动画效果优化
|
||||
- [x] 响应式适配完成
|
||||
|
||||
---
|
||||
|
||||
## 📞 后续优化建议
|
||||
|
||||
1. **性能优化**
|
||||
- 减少动画使用频率
|
||||
- 优化大图片加载
|
||||
- 使用 CSS containment
|
||||
|
||||
2. **无障碍改进**
|
||||
- 确保颜色对比度 ≥ 4.5:1
|
||||
- 添加 aria 标签
|
||||
- 支持高对比度模式
|
||||
|
||||
3. **深色模式增强**
|
||||
- 添加"纯黑"和"深灰"两种模式切换
|
||||
- 保存用户偏好设置
|
||||
|
||||
4. **主题扩展**
|
||||
- 支持自定义金色色相
|
||||
- 添加更多预设主题配色
|
||||
|
||||
---
|
||||
|
||||
**更新日期**: 2025-01-XX
|
||||
**版本**: v3.0.0
|
||||
**主题**: 黑金奢华 (Black & Gold Luxury)
|
||||
|
|
@ -0,0 +1,432 @@
|
|||
# 新用户引导功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
为首页添加了新用户引导功能,首次访问小程序时会自动显示,帮助用户快速了解核心功能。
|
||||
|
||||
---
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 1. 引导组件 (`components/guide/guide`)
|
||||
|
||||
**核心特性**:
|
||||
- ✅ 高亮遮罩层(打洞效果)
|
||||
- ✅ 自适应位置计算
|
||||
- ✅ 多步骤引导流程
|
||||
- ✅ 进度指示器
|
||||
- ✅ 平滑过渡动画
|
||||
- ✅ 防止穿透交互
|
||||
|
||||
**文件结构**:
|
||||
```
|
||||
components/guide/
|
||||
├── guide.wxml # 模板(遮罩 + 高亮 + 提示卡片)
|
||||
├── guide.wxss # 样式(渐变背景 + 动画)
|
||||
├── guide.js # 逻辑(位置计算 + 步骤控制)
|
||||
└── guide.json # 配置
|
||||
```
|
||||
|
||||
### 2. 引导步骤配置
|
||||
|
||||
在 [pages/index/index.js](pages/index/index.js) 中定义引导步骤:
|
||||
|
||||
```javascript
|
||||
guideSteps: [
|
||||
{
|
||||
selector: '.guide-create-btn', // 目标元素选择器
|
||||
title: '创建房间', // 标题
|
||||
description: '点击这里可以...', // 说明文字
|
||||
position: 'bottom', // 提示框位置(top/bottom/left/right)
|
||||
padding: 12, // 高亮区域扩展边距
|
||||
borderRadius: 16 // 高亮区域圆角
|
||||
},
|
||||
// ... 更多步骤
|
||||
]
|
||||
```
|
||||
|
||||
### 3. 首次访问检测
|
||||
|
||||
使用 `wx.getStorageSync('hasShownGuide')` 检测是否已显示过引导:
|
||||
|
||||
```javascript
|
||||
checkAndShowGuide() {
|
||||
const hasShownGuide = wx.getStorageSync('hasShownGuide')
|
||||
if (!hasShownGuide) {
|
||||
// 延迟500ms显示,等待页面完全渲染
|
||||
setTimeout(() => {
|
||||
this.setData({ showGuide: true })
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 目标元素标记
|
||||
|
||||
为需要引导的元素添加唯一的class:
|
||||
|
||||
```html
|
||||
<!-- 创建房间按钮 -->
|
||||
<view class="action-btn primary-btn guide-create-btn" bindtap="createRoom">
|
||||
<!-- ... -->
|
||||
</view>
|
||||
|
||||
<!-- 加入房间按钮 -->
|
||||
<view class="action-btn secondary-btn guide-join-btn" bindtap="joinRoom">
|
||||
<!-- ... -->
|
||||
</view>
|
||||
|
||||
<!-- 扫码按钮 -->
|
||||
<view class="scan-btn guide-scan-btn" bindtap="scanCode">
|
||||
<!-- ... -->
|
||||
</view>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 在其他页面中使用
|
||||
|
||||
#### 1. 注册组件
|
||||
|
||||
在页面的 `xxx.json` 中:
|
||||
|
||||
```json
|
||||
{
|
||||
"usingComponents": {
|
||||
"user-guide": "../../components/guide/guide"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 添加模板
|
||||
|
||||
在页面的 `xxx.wxml` 中:
|
||||
|
||||
```html
|
||||
<user-guide
|
||||
show="{{showGuide}}"
|
||||
steps="{{guideSteps}}"
|
||||
bind:complete="onGuideComplete"
|
||||
/>
|
||||
```
|
||||
|
||||
#### 3. 配置步骤
|
||||
|
||||
在页面的 `xxx.js` 中:
|
||||
|
||||
```javascript
|
||||
Page({
|
||||
data: {
|
||||
showGuide: false,
|
||||
guideSteps: [
|
||||
{
|
||||
selector: '.target-element',
|
||||
title: '功能标题',
|
||||
description: '功能说明文字',
|
||||
position: 'bottom', // 可选:top/bottom/left/right
|
||||
padding: 12, // 可选:高亮区域扩展边距(默认8)
|
||||
borderRadius: 16, // 可选:圆角大小(默认16)
|
||||
showArrow: true // 可选:是否显示箭头(默认true)
|
||||
}
|
||||
// ... 更多步骤
|
||||
]
|
||||
},
|
||||
|
||||
onReady() {
|
||||
// 检查是否需要显示引导
|
||||
const hasShown = wx.getStorageSync('hasShownGuide_pageName')
|
||||
if (!hasShown) {
|
||||
setTimeout(() => {
|
||||
this.setData({ showGuide: true })
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
|
||||
onGuideComplete() {
|
||||
this.setData({ showGuide: false })
|
||||
wx.setStorageSync('hasShownGuide_pageName', true)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 测试方法
|
||||
|
||||
### 方法1:清除存储测试
|
||||
|
||||
在微信开发者工具的控制台中执行:
|
||||
|
||||
```javascript
|
||||
// 清除引导标记
|
||||
wx.removeStorageSync('hasShownGuide')
|
||||
|
||||
// 重新加载页面
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
```
|
||||
|
||||
### 方法2:清除缓存测试
|
||||
|
||||
1. 微信开发者工具 → 清缓存 → 清除所有
|
||||
2. 重新编译项目
|
||||
3. 重新进入首页
|
||||
|
||||
### 方法3:手动触发测试
|
||||
|
||||
在 [pages/index/index.js](pages/index/index.js) 中临时修改:
|
||||
|
||||
```javascript
|
||||
checkAndShowGuide() {
|
||||
// 注释掉检查逻辑,直接显示
|
||||
// const hasShownGuide = wx.getStorageSync('hasShownGuide')
|
||||
// if (!hasShownGuide) {
|
||||
setTimeout(() => {
|
||||
this.setData({ showGuide: true })
|
||||
}, 500)
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 自定义配置
|
||||
|
||||
### 修改引导步骤
|
||||
|
||||
编辑 [pages/index/index.js](pages/index/index.js) 中的 `guideSteps` 数组:
|
||||
|
||||
```javascript
|
||||
guideSteps: [
|
||||
// 添加新的步骤
|
||||
{
|
||||
selector: '.my-element',
|
||||
title: '新功能',
|
||||
description: '这是一个新功能的说明',
|
||||
position: 'top',
|
||||
padding: 10,
|
||||
borderRadius: 12
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 修改样式
|
||||
|
||||
编辑 [components/guide/guide.wxss](components/guide/guide.wxss):
|
||||
|
||||
```css
|
||||
/* 修改遮罩透明度 */
|
||||
.guide-overlay {
|
||||
background: rgba(0, 0, 0, 0.75); /* 调整透明度 */
|
||||
}
|
||||
|
||||
/* 修改提示卡片样式 */
|
||||
.guide-text-wrapper {
|
||||
background: white; /* 背景色 */
|
||||
border-radius: 16rpx; /* 圆角 */
|
||||
padding: 32rpx 28rpx; /* 内边距 */
|
||||
}
|
||||
|
||||
/* 修改按钮样式 */
|
||||
.guide-next {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
```
|
||||
|
||||
### 修改位置计算逻辑
|
||||
|
||||
编辑 [components/guide/guide.js](components/guide/guide.js) 中的 `calculateContentPosition` 方法。
|
||||
|
||||
---
|
||||
|
||||
## 功能特点
|
||||
|
||||
### 1. 智能位置计算
|
||||
|
||||
组件会自动计算最佳显示位置,避免超出屏幕边界:
|
||||
|
||||
- 优先使用指定位置(top/bottom/left/right)
|
||||
- 空间不足时自动切换到对面
|
||||
- 保持在屏幕可视范围内
|
||||
|
||||
### 2. 高亮打洞效果
|
||||
|
||||
使用 CSS `box-shadow` 实现高亮区域:
|
||||
|
||||
```css
|
||||
.guide-spotlight {
|
||||
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
```
|
||||
|
||||
这样可以在遮罩层上"打洞",突出显示目标元素。
|
||||
|
||||
### 3. 步骤进度指示
|
||||
|
||||
底部圆点显示当前进度:
|
||||
|
||||
- 灰色圆点:未完成的步骤
|
||||
- 紫色长条:当前步骤
|
||||
- 平滑过渡动画
|
||||
|
||||
### 4. 防止交互穿透
|
||||
|
||||
```javascript
|
||||
// 阻止滚动
|
||||
preventMove() {}
|
||||
|
||||
// 阻止点击穿透
|
||||
catchtouchmove="preventMove"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 1. 选择器必须唯一
|
||||
|
||||
确保每个引导步骤的 `selector` 能唯一定位到目标元素:
|
||||
|
||||
```html
|
||||
<!-- ✅ 正确:使用唯一的class -->
|
||||
<view class="btn guide-create-btn"></view>
|
||||
|
||||
<!-- ❌ 错误:选择器不唯一 -->
|
||||
<view class="btn"></view>
|
||||
<view class="btn"></view>
|
||||
```
|
||||
|
||||
### 2. 等待页面渲染
|
||||
|
||||
在 `onReady` 生命周期中显示引导,并使用延迟:
|
||||
|
||||
```javascript
|
||||
onReady() {
|
||||
setTimeout(() => {
|
||||
this.setData({ showGuide: true })
|
||||
}, 500) // 延迟500ms,确保页面完全渲染
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 避免在隐藏元素上使用
|
||||
|
||||
不要在 `wx:if="{{false}}"` 或 `display: none` 的元素上使用引导,否则无法获取位置。
|
||||
|
||||
### 4. 不同页面使用不同标记
|
||||
|
||||
如果多个页面都有引导,使用不同的storage key:
|
||||
|
||||
```javascript
|
||||
// 首页
|
||||
wx.setStorageSync('hasShownGuide_index', true)
|
||||
|
||||
// 其他页面
|
||||
wx.setStorageSync('hasShownGuide_detail', true)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API参数
|
||||
|
||||
### 组件属性
|
||||
|
||||
| 属性 | 类型 | 必需 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| show | Boolean | 是 | false | 是否显示引导 |
|
||||
| steps | Array | 是 | [] | 引导步骤配置 |
|
||||
|
||||
### 组件事件
|
||||
|
||||
| 事件 | 参数 | 说明 |
|
||||
|------|------|------|
|
||||
| complete | 无 | 引导完成时触发 |
|
||||
|
||||
### 步骤配置
|
||||
|
||||
| 字段 | 类型 | 必需 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| selector | String | 是 | - | 目标元素的CSS选择器 |
|
||||
| title | String | 是 | - | 引导标题 |
|
||||
| description | String | 是 | - | 引导说明文字 |
|
||||
| position | String | 否 | 'bottom' | 提示框位置(top/bottom/left/right)|
|
||||
| padding | Number | 否 | 8 | 高亮区域扩展边距(px)|
|
||||
| borderRadius | Number | 否 | 16 | 高亮区域圆角大小(px)|
|
||||
| showArrow | Boolean | 否 | true | 是否显示箭头 |
|
||||
|
||||
---
|
||||
|
||||
## 效果预览
|
||||
|
||||
### 步骤1:创建房间
|
||||
- 高亮"创建房间"按钮
|
||||
- 显示说明文字
|
||||
- 进度指示:1/3
|
||||
|
||||
### 步骤2:加入房间
|
||||
- 高亮"加入房间"按钮
|
||||
- 显示说明文字
|
||||
- 进度指示:2/3
|
||||
|
||||
### 步骤3:扫码进入
|
||||
- 高亮"扫码"按钮
|
||||
- 显示说明文字
|
||||
- 进度指示:3/3
|
||||
- 显示"知道了"按钮
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 引导没有显示?
|
||||
|
||||
A: 检查以下几点:
|
||||
1. 是否已经显示过(清除 storage)
|
||||
2. 选择器是否正确
|
||||
3. 目标元素是否已渲染
|
||||
4. 延迟时间是否足够
|
||||
|
||||
### Q: 位置显示不对?
|
||||
|
||||
A: 检查:
|
||||
1. 目标元素是否被隐藏
|
||||
2. 页面是否完全渲染
|
||||
3. 增加 `setTimeout` 延迟时间
|
||||
|
||||
### Q: 如何调试?
|
||||
|
||||
A: 在组件的 `showStep` 方法中添加日志:
|
||||
|
||||
```javascript
|
||||
showStep(stepIndex) {
|
||||
const step = this.data.steps[stepIndex]
|
||||
console.log('显示引导步骤:', stepIndex, step)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 如何强制重新显示引导?
|
||||
|
||||
A: 控制台执行:
|
||||
|
||||
```javascript
|
||||
wx.removeStorageSync('hasShownGuide')
|
||||
getCurrentPages()[0].setData({ showGuide: true })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 未来优化方向
|
||||
|
||||
1. **手势控制** - 支持滑动切换步骤
|
||||
2. **视频教程** - 嵌入视频演示
|
||||
3. **自定义主题** - 支持暗色模式
|
||||
4. **动画增强** - 添加更多过渡动画
|
||||
5. **智能触发** - 根据用户行为动态显示引导
|
||||
6. **A/B测试** - 支持多种引导方案
|
||||
|
||||
---
|
||||
|
||||
## 开发者
|
||||
|
||||
如有问题或建议,请联系开发团队。
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
import { injectApp, waitLogin, gatewayHttpClient } from '@jdmini/api'
|
||||
import request from './utils/request'
|
||||
|
||||
App(injectApp()({
|
||||
globalData: {
|
||||
userInfo: null,
|
||||
currentSession: null,
|
||||
openid: null,
|
||||
jdToken: null
|
||||
},
|
||||
|
||||
async getQrcode(scene, page = 'pages/game/join/join') {
|
||||
try {
|
||||
const response = await gatewayHttpClient.request('/wx/v1/api/app/qrcode', 'POST', {
|
||||
scene: scene,
|
||||
page: page,
|
||||
check_path: false, // 关闭路径检查,因为可能是动态路由
|
||||
env_version: 'release' // 开发版本
|
||||
},
|
||||
{
|
||||
responseType: 'arraybuffer'
|
||||
}
|
||||
)
|
||||
console.log('二维码API响应:', response)
|
||||
// 检查响应是否是对象,如果是则可能包含data字段
|
||||
if (response && typeof response === 'object' && response.data) {
|
||||
return response.data
|
||||
}
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error('获取二维码失败:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
async onLaunch() {
|
||||
console.log('App启动')
|
||||
|
||||
// 等待JD登录完成,获取openid
|
||||
await this.initJDLogin()
|
||||
|
||||
// JD登录完成后,尝试后端登录
|
||||
//await this.loginToBackend()
|
||||
// console.log(await this.getQrcode('a=1&b=2'))
|
||||
},
|
||||
|
||||
// 初始化JD登录,获取openid和token
|
||||
async initJDLogin() {
|
||||
try {
|
||||
// 等待JD登录完成
|
||||
await waitLogin()
|
||||
|
||||
// 获取JD用户信息
|
||||
const jdToken = wx.getStorageSync('jdwx-token')
|
||||
const jdUserInfo = wx.getStorageSync('jdwx-userinfo')
|
||||
|
||||
if (jdUserInfo && jdUserInfo.openId) {
|
||||
this.globalData.openid = jdUserInfo.openId
|
||||
this.globalData.jdToken = jdToken
|
||||
console.log('JD登录成功,openid:', jdUserInfo.openId)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('JD登录失败:', error)
|
||||
}
|
||||
},
|
||||
|
||||
// 登录到后端
|
||||
async loginToBackend() {
|
||||
try {
|
||||
if (!this.globalData.openid) {
|
||||
console.log('没有openid,跳过后端登录')
|
||||
return false
|
||||
}
|
||||
|
||||
console.log('尝试后端登录,openid:', this.globalData.openid)
|
||||
|
||||
// 调用后端登录接口(不传userInfo,只用openid查询)
|
||||
const response = await request.post('/auth/login', {
|
||||
openid: this.globalData.openid
|
||||
})
|
||||
|
||||
if (response && response.player) {
|
||||
// 用户已注册,保存用户信息
|
||||
this.globalData.userInfo = response.player
|
||||
wx.setStorageSync('userInfo', response.player)
|
||||
console.log('后端登录成功,用户信息:', response.player)
|
||||
|
||||
// 触发登录成功事件,通知首页更新
|
||||
this.triggerLoginSuccess()
|
||||
return true
|
||||
} else if (response && response.exists === false) {
|
||||
// 用户未注册(后端返回 player: null, exists: false)
|
||||
console.log('用户未注册,需要完善信息')
|
||||
return false
|
||||
} else {
|
||||
// 其他情况
|
||||
console.log('登录响应异常:', response)
|
||||
return false
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('后端登录失败:', error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
// 触发登录成功事件
|
||||
triggerLoginSuccess() {
|
||||
this.refreshIndexPage()
|
||||
},
|
||||
|
||||
// 刷新首页数据
|
||||
refreshIndexPage() {
|
||||
// 获取所有页面
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 0) {
|
||||
// 查找首页
|
||||
const indexPage = pages.find(page => page.route === 'pages/index/index')
|
||||
if (indexPage && typeof indexPage.loadUserInfo === 'function') {
|
||||
console.log('触发首页刷新')
|
||||
indexPage.loadUserInfo()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 获取当前用户信息
|
||||
getUserInfo() {
|
||||
return wx.getStorageSync('userInfo')
|
||||
},
|
||||
getUserOpenid() {
|
||||
return this.globalData.openid
|
||||
},
|
||||
|
||||
// 设置用户信息
|
||||
setUserInfo(userInfo) {
|
||||
this.globalData.userInfo = userInfo
|
||||
wx.setStorageSync('userInfo', userInfo)
|
||||
},
|
||||
|
||||
// 更新当前牌局
|
||||
setCurrentSession(session) {
|
||||
this.globalData.currentSession = session
|
||||
},
|
||||
|
||||
// 获取当前牌局
|
||||
getCurrentSession() {
|
||||
return this.globalData.currentSession
|
||||
}
|
||||
}))
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"pages": [
|
||||
"pages/index/index",
|
||||
"pages/login/login",
|
||||
"pages/game/create/create",
|
||||
"pages/game/join/join",
|
||||
"pages/game/detail/detail",
|
||||
"pages/game/settlement/settlement",
|
||||
"pages/game/play/play",
|
||||
"pages/record/list/list",
|
||||
"pages/stats/personal/personal",
|
||||
"pages/stats/session/session",
|
||||
"pages/profile/index/index"
|
||||
],
|
||||
"window": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "打牌记账小本",
|
||||
"navigationBarBackgroundColor": "#07c160"
|
||||
},
|
||||
"tabBar": {
|
||||
"custom": true,
|
||||
"color": "#666666",
|
||||
"selectedColor": "#667eea",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/stats/personal/personal",
|
||||
"text": "统计"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/profile/index/index",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"style": "v2",
|
||||
"componentFramework": "glass-easel",
|
||||
"sitemapLocation": "sitemap.json",
|
||||
"lazyCodeLoading": "requiredComponents"
|
||||
}
|
||||
|
|
@ -0,0 +1,527 @@
|
|||
/**
|
||||
* h@7 - ÑÑbN;˜
|
||||
* ;rÑr (#D4AF37) + Ñr (#000000)
|
||||
* Î<bNØï¾ô
|
||||
*/
|
||||
|
||||
/* e;˜Mn */
|
||||
@import "styles/theme.wxss";
|
||||
|
||||
/* ==================== h@ú@7 ==================== */
|
||||
|
||||
page {
|
||||
background: linear-gradient(180deg, #000000 0%, #0A0A0A 100%);
|
||||
color: #FFFFFF;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ==================== ®ûß ==================== */
|
||||
|
||||
/* ; ® - ÑrØ */
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #D4AF37 0%, #F4E6C3 100%);
|
||||
color: #000000;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(212, 175, 55, 0.4);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.btn-primary:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4rpx 16rpx rgba(212, 175, 55, 0.5);
|
||||
}
|
||||
|
||||
/* !<21> ® - örØ */
|
||||
.btn-secondary {
|
||||
background: linear-gradient(135deg, #C0C0C0 0%, #E5E5E5 100%);
|
||||
color: #000000;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(192, 192, 192, 0.3);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.btn-secondary:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4rpx 16rpx rgba(192, 192, 192, 0.4);
|
||||
}
|
||||
|
||||
/* Ÿ ® - ÿr */
|
||||
.btn-success {
|
||||
background: linear-gradient(135deg, #50C878 0%, #06a854 100%);
|
||||
color: #FFFFFF;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(80, 200, 120, 0.3);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.btn-success:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4rpx 16rpx rgba(80, 200, 120, 0.4);
|
||||
}
|
||||
|
||||
/* fJ ® - Ñr */
|
||||
.btn-warning {
|
||||
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
|
||||
color: #000000;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(255, 215, 0, 0.3);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.btn-warning:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4rpx 16rpx rgba(255, 215, 0, 0.4);
|
||||
}
|
||||
|
||||
/* qi ® - ¢r */
|
||||
.btn-danger {
|
||||
background: linear-gradient(135deg, #DC143C 0%, #8B0000 100%);
|
||||
color: #FFFFFF;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(220, 20, 60, 0.3);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.btn-danger:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4rpx 16rpx rgba(220, 20, 60, 0.4);
|
||||
}
|
||||
|
||||
/* }u ® - Ñr¹F */
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
color: #D4AF37;
|
||||
border: 2rpx solid #D4AF37;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.btn-ghost:active {
|
||||
background: rgba(212, 175, 55, 0.1);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* <20>( ® */
|
||||
.btn-disabled,
|
||||
button[disabled] {
|
||||
background: #2A2A2A !important;
|
||||
color: #606060 !important;
|
||||
box-shadow: none !important;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* ==================== aGûß ==================== */
|
||||
|
||||
/* ÆaG */
|
||||
.card {
|
||||
background: linear-gradient(135deg, #1A1A1A 0%, #252525 100%);
|
||||
border-radius: 20rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.8);
|
||||
border: 1rpx solid rgba(212, 175, 55, 0.2);
|
||||
margin-bottom: 24rpx;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.card:active {
|
||||
transform: translateY(-2rpx);
|
||||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
/* ÑrÅpaG */
|
||||
.card-gold {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-gold::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3rpx;
|
||||
background: linear-gradient(90deg, transparent 0%, #D4AF37 20%, #F4E6C3 50%, #D4AF37 80%, transparent 100%);
|
||||
}
|
||||
|
||||
/* خaG */
|
||||
.card-highlight {
|
||||
background: linear-gradient(135deg, #252525 0%, #2A2A2A 100%);
|
||||
border: 2rpx solid #D4AF37;
|
||||
box-shadow: 0 4rpx 20rpx rgba(212, 175, 55, 0.3);
|
||||
}
|
||||
|
||||
/* ==================== 8øs ==================== */
|
||||
|
||||
/* Ü)7 */
|
||||
.game-win {
|
||||
color: #50C878;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.game-win-bg {
|
||||
background: linear-gradient(135deg, rgba(80, 200, 120, 0.15) 0%, rgba(6, 168, 84, 0.2) 100%);
|
||||
border: 1rpx solid rgba(80, 200, 120, 0.3);
|
||||
}
|
||||
|
||||
/* 1%7 */
|
||||
.game-lose {
|
||||
color: #DC143C;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.game-lose-bg {
|
||||
background: linear-gradient(135deg, rgba(220, 20, 60, 0.15) 0%, rgba(139, 0, 0, 0.2) 100%);
|
||||
border: 1rpx solid rgba(220, 20, 60, 0.3);
|
||||
}
|
||||
|
||||
/* s@7 */
|
||||
.game-draw {
|
||||
color: #B0B0B0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.game-draw-bg {
|
||||
background: linear-gradient(135deg, rgba(176, 176, 176, 0.15) 0%, rgba(96, 96, 96, 0.2) 100%);
|
||||
border: 1rpx solid rgba(176, 176, 176, 0.3);
|
||||
}
|
||||
|
||||
/* ==================== h7 ==================== */
|
||||
|
||||
.list-item {
|
||||
background: #1A1A1A;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1rpx solid #2A2A2A;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.list-item:active {
|
||||
background: #222222;
|
||||
transform: translateX(8rpx);
|
||||
}
|
||||
|
||||
.list-item-content {
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.list-item-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.list-item-desc {
|
||||
font-size: 24rpx;
|
||||
color: #B0B0B0;
|
||||
}
|
||||
|
||||
/* ==================== 4Ïûß ==================== */
|
||||
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #2A2A2A 0%, #3A3A3A 100%);
|
||||
border: 3rpx solid #D4AF37;
|
||||
box-shadow: 0 4rpx 12rpx rgba(212, 175, 55, 0.4);
|
||||
}
|
||||
|
||||
.avatar-sm {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
|
||||
.avatar-md {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.avatar-lg {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
}
|
||||
|
||||
.avatar-xl {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
/* ==================== ½à~ ==================== */
|
||||
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 20rpx;
|
||||
font-weight: 600;
|
||||
border: 1rpx solid;
|
||||
}
|
||||
|
||||
.badge-gold {
|
||||
background: linear-gradient(135deg, rgba(212, 175, 55, 0.15) 0%, rgba(244, 230, 195, 0.2) 100%);
|
||||
color: #D4AF37;
|
||||
border-color: #D4AF37;
|
||||
}
|
||||
|
||||
.badge-silver {
|
||||
background: rgba(192, 192, 192, 0.15);
|
||||
color: #C0C0C0;
|
||||
border-color: #C0C0C0;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: rgba(80, 200, 120, 0.15);
|
||||
color: #50C878;
|
||||
border-color: #50C878;
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background: rgba(255, 215, 0, 0.15);
|
||||
color: #FFD700;
|
||||
border-color: #FFD700;
|
||||
}
|
||||
|
||||
.badge-danger {
|
||||
background: rgba(220, 20, 60, 0.15);
|
||||
color: #DC143C;
|
||||
border-color: #DC143C;
|
||||
}
|
||||
|
||||
/* ==================== !F/9— ==================== */
|
||||
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 600rpx;
|
||||
max-width: 90%;
|
||||
background: linear-gradient(135deg, #1A1A1A 0%, #252525 100%);
|
||||
border: 1rpx solid rgba(212, 175, 55, 0.3);
|
||||
border-radius: 24rpx;
|
||||
padding: 48rpx 32rpx 32rpx;
|
||||
box-shadow: 0 16rpx 64rpx rgba(0, 0, 0, 0.9);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #F4E6C3;
|
||||
text-align: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
font-size: 28rpx;
|
||||
color: #E0E0E0;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.modal-footer button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* ==================== z¶ ==================== */
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
color: #3A3A3A;
|
||||
margin-bottom: 32rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #B0B0B0;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
font-size: 24rpx;
|
||||
color: #606060;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
/* ==================== }¶ ==================== */
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80rpx 0;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 4rpx solid #2A2A2A;
|
||||
border-top-color: #D4AF37;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 24rpx;
|
||||
font-size: 24rpx;
|
||||
color: #B0B0B0;
|
||||
}
|
||||
|
||||
/* ==================== •èÍ\ ==================== */
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #1A1A1A;
|
||||
border-top: 1rpx solid rgba(212, 175, 55, 0.2);
|
||||
padding: 24rpx 32rpx;
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.8);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.bottom-bar-content {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ==================== ’
½à ==================== */
|
||||
|
||||
.rank-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
border: 3rpx solid;
|
||||
}
|
||||
|
||||
.rank-1 {
|
||||
background: linear-gradient(135deg, #D4AF37 0%, #F4E6C3 100%);
|
||||
color: #000000;
|
||||
border-color: #D4AF37;
|
||||
box-shadow: 0 4rpx 16rpx rgba(212, 175, 55, 0.5);
|
||||
}
|
||||
|
||||
.rank-2 {
|
||||
background: linear-gradient(135deg, #C0C0C0 0%, #E5E5E5 100%);
|
||||
color: #000000;
|
||||
border-color: #C0C0C0;
|
||||
box-shadow: 0 4rpx 16rpx rgba(192, 192, 192, 0.4);
|
||||
}
|
||||
|
||||
.rank-3 {
|
||||
background: linear-gradient(135deg, #CD7F32 0%, #E5A35C 100%);
|
||||
color: #000000;
|
||||
border-color: #CD7F32;
|
||||
box-shadow: 0 4rpx 16rpx rgba(205, 127, 50, 0.4);
|
||||
}
|
||||
|
||||
.rank-other {
|
||||
background: #2A2A2A;
|
||||
color: #B0B0B0;
|
||||
border-color: #3A3A3A;
|
||||
}
|
||||
|
||||
/* ==================== r¿ ==================== */
|
||||
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background: #2A2A2A;
|
||||
margin: 24rpx 0;
|
||||
}
|
||||
|
||||
.divider-gold {
|
||||
height: 2rpx;
|
||||
background: linear-gradient(90deg, transparent 0%, #D4AF37 20%, #D4AF37 80%, transparent 100%);
|
||||
margin: 32rpx 0;
|
||||
}
|
||||
|
||||
/* ==================== ž(åw{ ==================== */
|
||||
|
||||
.text-center { text-align: center; }
|
||||
.text-left { text-align: left; }
|
||||
.text-right { text-align: right; }
|
||||
|
||||
.mt-xs { margin-top: 8rpx; }
|
||||
.mt-sm { margin-top: 12rpx; }
|
||||
.mt-md { margin-top: 16rpx; }
|
||||
.mt-lg { margin-top: 24rpx; }
|
||||
.mt-xl { margin-top: 32rpx; }
|
||||
|
||||
.mb-xs { margin-bottom: 8rpx; }
|
||||
.mb-sm { margin-bottom: 12rpx; }
|
||||
.mb-md { margin-bottom: 16rpx; }
|
||||
.mb-lg { margin-bottom: 24rpx; }
|
||||
.mb-xl { margin-bottom: 32rpx; }
|
||||
|
||||
.p-xs { padding: 8rpx; }
|
||||
.p-sm { padding: 12rpx; }
|
||||
.p-md { padding: 16rpx; }
|
||||
.p-lg { padding: 24rpx; }
|
||||
.p-xl { padding: 32rpx; }
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// components/custom-tabbar/custom-tabbar.js
|
||||
Component({
|
||||
data: {
|
||||
selected: 0,
|
||||
color: "#666666",
|
||||
selectedColor: "#667eea",
|
||||
list: [
|
||||
{
|
||||
pagePath: "/pages/index/index",
|
||||
text: "打牌记账",
|
||||
iconPath: "../images/index.png",
|
||||
selectedIconPath: "../images/index_active.png"
|
||||
},
|
||||
{
|
||||
pagePath: "/pages/stats/personal/personal",
|
||||
text: "统计分析",
|
||||
iconPath: "../images/record.png",
|
||||
selectedIconPath: "../images/record-active.png"
|
||||
},
|
||||
{
|
||||
pagePath: "/pages/profile/index/index",
|
||||
text: "我的",
|
||||
iconPath: "../images/my.png",
|
||||
selectedIconPath: "../images/my-active.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
properties: {
|
||||
selected: {
|
||||
type: Number,
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
// 组件初始化时更新选中状态
|
||||
this.setData({
|
||||
selected: this.data.selected
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
switchTab(e) {
|
||||
const index = e.currentTarget.dataset.index
|
||||
const pagePath = this.data.list[index].pagePath
|
||||
|
||||
// 触发事件通知父组件
|
||||
this.triggerEvent('tabchange', { index, pagePath })
|
||||
|
||||
// 切换页面
|
||||
wx.switchTab({
|
||||
url: pagePath
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<!-- components/custom-tabbar/custom-tabbar.wxml -->
|
||||
<view class="tabbar">
|
||||
<view
|
||||
class="tabbar-item {{selected === index ? 'active' : ''}}"
|
||||
wx:for="{{list}}"
|
||||
wx:key="index"
|
||||
data-index="{{index}}"
|
||||
bindtap="switchTab"
|
||||
>
|
||||
<view class="tabbar-icon">
|
||||
<image
|
||||
src="{{selected === index ? item.selectedIconPath : item.iconPath}}"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<view
|
||||
class="tabbar-text"
|
||||
style="color: {{selected === index ? selectedColor : color}}"
|
||||
>
|
||||
{{item.text}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/* components/custom-tabbar/custom-tabbar.wxss */
|
||||
.tabbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 100rpx;
|
||||
background: #1A1A1A;
|
||||
border-top: 1rpx solid rgba(212, 175, 55, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.8);
|
||||
z-index: 999;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10rpx 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tabbar-item.active {
|
||||
transform: translateY(-4rpx);
|
||||
}
|
||||
|
||||
.tabbar-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.tabbar-icon image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tabbar-text {
|
||||
font-size: 22rpx;
|
||||
color: #B0B0B0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tabbar-item.active .tabbar-text {
|
||||
font-weight: 600;
|
||||
font-size: 24rpx;
|
||||
color: #D4AF37;
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
// 新用户引导组件
|
||||
Component({
|
||||
properties: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
steps: {
|
||||
type: Array,
|
||||
value: []
|
||||
}
|
||||
},
|
||||
|
||||
data: {
|
||||
currentStep: 0,
|
||||
totalSteps: 0,
|
||||
title: '',
|
||||
description: '',
|
||||
spotlightStyle: {},
|
||||
contentStyle: '',
|
||||
arrowDirection: 'top',
|
||||
showArrow: true
|
||||
},
|
||||
|
||||
observers: {
|
||||
'show': function(show) {
|
||||
if (show && this.data.steps.length > 0) {
|
||||
this.setData({
|
||||
currentStep: 0,
|
||||
totalSteps: this.data.steps.length
|
||||
})
|
||||
this.showStep(0)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 显示指定步骤
|
||||
showStep(stepIndex) {
|
||||
if (stepIndex < 0 || stepIndex >= this.data.steps.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const step = this.data.steps[stepIndex]
|
||||
this.setData({
|
||||
currentStep: stepIndex,
|
||||
title: step.title,
|
||||
description: step.description,
|
||||
showArrow: step.showArrow !== false
|
||||
})
|
||||
|
||||
// 延迟获取元素位置,确保页面已渲染
|
||||
setTimeout(() => {
|
||||
this.updateSpotlight(step)
|
||||
}, 50)
|
||||
},
|
||||
|
||||
// 更新高亮区域
|
||||
updateSpotlight(step) {
|
||||
const query = wx.createSelectorQuery().in(this)
|
||||
|
||||
// 查询目标元素(在页面中)
|
||||
const pageQuery = wx.createSelectorQuery()
|
||||
pageQuery.select(step.selector).boundingClientRect()
|
||||
pageQuery.selectViewport().scrollOffset()
|
||||
|
||||
pageQuery.exec((res) => {
|
||||
if (!res || !res[0]) {
|
||||
console.warn('未找到目标元素:', step.selector)
|
||||
return
|
||||
}
|
||||
|
||||
const rect = res[0]
|
||||
const scroll = res[1]
|
||||
|
||||
// 计算高亮区域样式(添加padding扩展区域)
|
||||
const padding = step.padding || 8
|
||||
const spotlightStyle = {
|
||||
top: rect.top - padding,
|
||||
left: rect.left - padding,
|
||||
width: rect.width + padding * 2,
|
||||
height: rect.height + padding * 2,
|
||||
borderRadius: step.borderRadius || 16
|
||||
}
|
||||
|
||||
// 计算提示内容位置
|
||||
const contentStyle = this.calculateContentPosition(spotlightStyle, step.position || 'bottom')
|
||||
|
||||
this.setData({
|
||||
spotlightStyle,
|
||||
contentStyle: contentStyle.style,
|
||||
arrowDirection: contentStyle.arrowDirection
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 计算提示内容位置
|
||||
calculateContentPosition(spotlightStyle, preferredPosition) {
|
||||
const windowInfo = wx.getWindowInfo()
|
||||
const screenHeight = windowInfo.windowHeight
|
||||
const screenWidth = windowInfo.windowWidth
|
||||
const contentWidth = 600 // rpx转换为px约300
|
||||
const contentMaxHeight = 400 // 估算最大高度
|
||||
|
||||
let position = preferredPosition
|
||||
let top = 0
|
||||
let left = 0
|
||||
let arrowDirection = 'top'
|
||||
|
||||
// 计算中心点
|
||||
const spotlightCenterX = spotlightStyle.left + spotlightStyle.width / 2
|
||||
const spotlightCenterY = spotlightStyle.top + spotlightStyle.height / 2
|
||||
|
||||
// 根据位置偏好计算
|
||||
switch (position) {
|
||||
case 'top':
|
||||
top = spotlightStyle.top - contentMaxHeight - 40
|
||||
left = Math.max(20, Math.min(spotlightCenterX - contentWidth / 2, screenWidth - contentWidth - 20))
|
||||
arrowDirection = 'bottom'
|
||||
|
||||
// 如果顶部空间不足,改为底部
|
||||
if (top < 20) {
|
||||
position = 'bottom'
|
||||
}
|
||||
break
|
||||
|
||||
case 'bottom':
|
||||
top = spotlightStyle.top + spotlightStyle.height + 20
|
||||
left = Math.max(20, Math.min(spotlightCenterX - contentWidth / 2, screenWidth - contentWidth - 20))
|
||||
arrowDirection = 'top'
|
||||
|
||||
// 如果底部空间不足,改为顶部
|
||||
if (top + contentMaxHeight > screenHeight - 20) {
|
||||
position = 'top'
|
||||
top = spotlightStyle.top - contentMaxHeight - 40
|
||||
arrowDirection = 'bottom'
|
||||
}
|
||||
break
|
||||
|
||||
case 'left':
|
||||
left = spotlightStyle.left - contentWidth - 20
|
||||
top = Math.max(20, Math.min(spotlightCenterY - 100, screenHeight - contentMaxHeight - 20))
|
||||
arrowDirection = 'right'
|
||||
|
||||
// 如果左侧空间不足,改为右侧
|
||||
if (left < 20) {
|
||||
position = 'right'
|
||||
}
|
||||
break
|
||||
|
||||
case 'right':
|
||||
left = spotlightStyle.left + spotlightStyle.width + 20
|
||||
top = Math.max(20, Math.min(spotlightCenterY - 100, screenHeight - contentMaxHeight - 20))
|
||||
arrowDirection = 'left'
|
||||
|
||||
// 如果右侧空间不足,改为左侧
|
||||
if (left + contentWidth > screenWidth - 20) {
|
||||
position = 'left'
|
||||
left = spotlightStyle.left - contentWidth - 20
|
||||
arrowDirection = 'right'
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// 如果重新计算了位置,递归调用
|
||||
if (position !== preferredPosition) {
|
||||
return this.calculateContentPosition(spotlightStyle, position)
|
||||
}
|
||||
|
||||
return {
|
||||
style: `top: ${top}px; left: ${left}px;`,
|
||||
arrowDirection
|
||||
}
|
||||
},
|
||||
|
||||
// 下一步
|
||||
handleNext() {
|
||||
const nextStep = this.data.currentStep + 1
|
||||
|
||||
if (nextStep >= this.data.steps.length) {
|
||||
// 引导完成
|
||||
this.handleComplete()
|
||||
} else {
|
||||
// 显示下一步
|
||||
this.showStep(nextStep)
|
||||
}
|
||||
},
|
||||
|
||||
// 跳过引导
|
||||
handleSkip() {
|
||||
this.handleComplete()
|
||||
},
|
||||
|
||||
// 完成引导
|
||||
handleComplete() {
|
||||
this.triggerEvent('complete')
|
||||
},
|
||||
|
||||
// 点击遮罩(不做任何操作,防止穿透)
|
||||
handleMaskTap() {
|
||||
// 可选:点击遮罩也进入下一步
|
||||
// this.handleNext()
|
||||
},
|
||||
|
||||
// 阻止滚动穿透
|
||||
preventMove() {}
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<!-- 新用户引导组件 -->
|
||||
<view class="guide-mask" wx:if="{{show}}" catchtouchmove="preventMove" bindtap="handleMaskTap">
|
||||
<!-- 遮罩层 -->
|
||||
<view class="guide-overlay"></view>
|
||||
|
||||
<!-- 高亮区域 -->
|
||||
<view class="guide-spotlight"
|
||||
style="top: {{spotlightStyle.top}}px; left: {{spotlightStyle.left}}px; width: {{spotlightStyle.width}}px; height: {{spotlightStyle.height}}px; border-radius: {{spotlightStyle.borderRadius}}px;">
|
||||
</view>
|
||||
|
||||
<!-- 提示内容 -->
|
||||
<view class="guide-content" style="{{contentStyle}}">
|
||||
<!-- 箭头指示器 -->
|
||||
<view class="guide-arrow {{arrowDirection}}" wx:if="{{showArrow}}"></view>
|
||||
|
||||
<!-- 文本内容 -->
|
||||
<view class="guide-text-wrapper">
|
||||
<view class="guide-title">{{title}}</view>
|
||||
<view class="guide-desc">{{description}}</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="guide-footer">
|
||||
<view class="guide-dots">
|
||||
<view class="guide-dot {{index === currentStep ? 'active' : ''}}"
|
||||
wx:for="{{totalSteps}}"
|
||||
wx:key="index">
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="guide-actions">
|
||||
<text class="guide-skip" bindtap="handleSkip" wx:if="{{currentStep < totalSteps - 1}}">跳过</text>
|
||||
<text class="guide-next" bindtap="handleNext">
|
||||
{{currentStep === totalSteps - 1 ? '知道了' : '下一步'}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/* 新用户引导样式 */
|
||||
|
||||
/* 遮罩层 */
|
||||
.guide-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.guide-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
/* 高亮区域(通过透明背景实现打洞效果)*/
|
||||
.guide-spotlight {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.75);
|
||||
z-index: 10000;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 提示内容容器 */
|
||||
.guide-content {
|
||||
position: absolute;
|
||||
z-index: 10001;
|
||||
max-width: 600rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.guide-text-wrapper {
|
||||
background: linear-gradient(135deg, #1A1A1A 0%, #252525 100%);
|
||||
border: 1rpx solid rgba(212, 175, 55, 0.3);
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx 28rpx 24rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
/* 箭头指示器 */
|
||||
.guide-arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 20rpx solid transparent;
|
||||
}
|
||||
|
||||
.guide-arrow.top {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -20rpx;
|
||||
border-bottom-color: #1A1A1A;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.guide-arrow.bottom {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -20rpx;
|
||||
border-top-color: #1A1A1A;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.guide-arrow.left {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
margin-top: -20rpx;
|
||||
border-right-color: #1A1A1A;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.guide-arrow.right {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
margin-top: -20rpx;
|
||||
border-left-color: #1A1A1A;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/* 文本样式 */
|
||||
.guide-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #F4E6C3;
|
||||
margin-bottom: 16rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.guide-desc {
|
||||
font-size: 26rpx;
|
||||
color: #E0E0E0;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.guide-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid rgba(212, 175, 55, 0.2);
|
||||
}
|
||||
|
||||
/* 进度点 */
|
||||
.guide-dots {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.guide-dot {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 50%;
|
||||
background: #3A3A3A;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.guide-dot.active {
|
||||
width: 32rpx;
|
||||
border-radius: 6rpx;
|
||||
background: linear-gradient(135deg, #D4AF37 0%, #F4E6C3 100%);
|
||||
box-shadow: 0 2rpx 8rpx rgba(212, 175, 55, 0.4);
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.guide-actions {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.guide-skip {
|
||||
font-size: 26rpx;
|
||||
color: #B0B0B0;
|
||||
padding: 8rpx 16rpx;
|
||||
}
|
||||
|
||||
.guide-next {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #000000;
|
||||
background: linear-gradient(135deg, #D4AF37 0%, #F4E6C3 100%);
|
||||
padding: 12rpx 32rpx;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(212, 175, 55, 0.4);
|
||||
}
|
||||
|
||||
.guide-skip:active {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.guide-next:active {
|
||||
opacity: 0.9;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 635 B |
|
After Width: | Height: | Size: 607 B |
|
|
@ -0,0 +1,75 @@
|
|||
// components/navbar/navbar.js
|
||||
Component({
|
||||
properties: {
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 是否显示返回按钮
|
||||
showBack: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
// 返回按钮文字
|
||||
backText: {
|
||||
type: String,
|
||||
value: '返回'
|
||||
},
|
||||
// 背景色(支持渐变)
|
||||
background: {
|
||||
type: String,
|
||||
value: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
|
||||
}
|
||||
},
|
||||
|
||||
data: {
|
||||
statusBarHeight: 0,
|
||||
menuButtonTop: 0,
|
||||
menuButtonHeight: 0,
|
||||
navbarHeight: 0
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.setNavBarInfo()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 设置导航栏信息
|
||||
setNavBarInfo() {
|
||||
const windowInfo = wx.getWindowInfo()
|
||||
const menuButtonInfo = wx.getMenuButtonBoundingClientRect()
|
||||
|
||||
const statusBarHeight = windowInfo.statusBarHeight
|
||||
const menuButtonTop = menuButtonInfo.top
|
||||
const menuButtonHeight = menuButtonInfo.height
|
||||
|
||||
// 导航栏总高度 = 胶囊按钮bottom + 胶囊按钮到顶部的距离
|
||||
const navbarHeight = menuButtonInfo.bottom + (menuButtonTop - statusBarHeight)
|
||||
|
||||
this.setData({
|
||||
statusBarHeight,
|
||||
menuButtonTop,
|
||||
menuButtonHeight,
|
||||
navbarHeight
|
||||
})
|
||||
},
|
||||
|
||||
// 返回按钮点击
|
||||
onBackTap() {
|
||||
this.triggerEvent('back')
|
||||
|
||||
// 如果没有监听back事件,默认返回上一页
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
wx.navigateBack()
|
||||
} else {
|
||||
wx.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<!-- 自定义导航栏组件 -->
|
||||
<view class="custom-navbar" style="height: {{navbarHeight}}px;">
|
||||
<!-- 返回按钮 -->
|
||||
<view
|
||||
class="nav-back-btn"
|
||||
wx:if="{{showBack}}"
|
||||
bindtap="onBackTap"
|
||||
style="top: {{menuButtonTop}}px; height: {{menuButtonHeight}}px;">
|
||||
<text class="back-icon">←</text>
|
||||
<text class="back-text" wx:if="{{backText}}">{{backText}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 标题 -->
|
||||
<view class="nav-title-wrap" style="height: {{menuButtonHeight}}px; top: {{menuButtonTop}}px;">
|
||||
<text class="nav-title">{{title}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/* 自定义导航栏样式 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
background: linear-gradient(135deg, #1A1A1A 0%, #252525 100%);
|
||||
border-bottom: 1rpx solid rgba(212, 175, 55, 0.2);
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
/* 返回按钮 */
|
||||
.nav-back-btn {
|
||||
position: fixed;
|
||||
left: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
padding: 0 24rpx;
|
||||
background: rgba(212, 175, 55, 0.15);
|
||||
border: 1rpx solid rgba(212, 175, 55, 0.3);
|
||||
border-radius: 50rpx;
|
||||
backdrop-filter: blur(10rpx);
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
font-size: 32rpx;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.back-text {
|
||||
font-size: 28rpx;
|
||||
color: white;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 标题区域 */
|
||||
.nav-title-wrap {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #F4E6C3;
|
||||
max-width: 60%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 创建SVG图标的函数
|
||||
function createSVGIcon(type, color) {
|
||||
let svgContent = '';
|
||||
|
||||
switch(type) {
|
||||
case 'home':
|
||||
svgContent = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M40.5 15 L20 35 L25 35 L25 65 L35 65 L35 50 L46 50 L46 65 L56 65 L56 35 L61 35 Z"
|
||||
fill="${color}" />
|
||||
</svg>`;
|
||||
break;
|
||||
|
||||
case 'stats':
|
||||
svgContent = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="15" y="45" width="15" height="20" fill="${color}"/>
|
||||
<rect x="33" y="35" width="15" height="30" fill="${color}"/>
|
||||
<rect x="51" y="25" width="15" height="40" fill="${color}"/>
|
||||
</svg>`;
|
||||
break;
|
||||
|
||||
case 'profile':
|
||||
svgContent = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="40.5" cy="25" r="12" fill="${color}"/>
|
||||
<ellipse cx="40.5" cy="55" rx="20" ry="15" fill="${color}"/>
|
||||
</svg>`;
|
||||
break;
|
||||
}
|
||||
|
||||
return svgContent;
|
||||
}
|
||||
|
||||
// 创建简单的PNG占位文件(使用最小的PNG数据)
|
||||
function createMinimalPNG() {
|
||||
// 最小的1x1 PNG图片的Base64数据
|
||||
const base64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
|
||||
return Buffer.from(base64, 'base64');
|
||||
}
|
||||
|
||||
// 图标配置
|
||||
const icons = [
|
||||
{ name: 'home.png', type: 'home', color: '#999999' },
|
||||
{ name: 'home-active.png', type: 'home', color: '#07c160' },
|
||||
{ name: 'stats.png', type: 'stats', color: '#999999' },
|
||||
{ name: 'stats-active.png', type: 'stats', color: '#07c160' },
|
||||
{ name: 'profile.png', type: 'profile', color: '#999999' },
|
||||
{ name: 'profile-active.png', type: 'profile', color: '#07c160' },
|
||||
{ name: 'default-avatar.png', type: 'profile', color: '#cccccc' },
|
||||
{ name: 'empty.png', type: 'home', color: '#dddddd' }
|
||||
];
|
||||
|
||||
// 创建images目录(如果不存在)
|
||||
const imagesDir = path.join(__dirname, 'images');
|
||||
if (!fs.existsSync(imagesDir)) {
|
||||
fs.mkdirSync(imagesDir, { recursive: true });
|
||||
}
|
||||
|
||||
// 创建所有图标文件(PNG占位符)
|
||||
icons.forEach(icon => {
|
||||
const filePath = path.join(imagesDir, icon.name);
|
||||
const pngData = createMinimalPNG();
|
||||
fs.writeFileSync(filePath, pngData);
|
||||
console.log(`创建图标: ${icon.name}`);
|
||||
});
|
||||
|
||||
// 同时创建SVG版本供参考
|
||||
icons.forEach(icon => {
|
||||
const svgFilePath = path.join(imagesDir, icon.name.replace('.png', '.svg'));
|
||||
const svgContent = createSVGIcon(icon.type, icon.color);
|
||||
if (svgContent) {
|
||||
fs.writeFileSync(svgFilePath, svgContent);
|
||||
console.log(`创建SVG: ${icon.name.replace('.png', '.svg')}`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n图标创建完成!');
|
||||
console.log('提示:');
|
||||
console.log('1. PNG文件是占位符,可以在微信开发者工具中正常运行');
|
||||
console.log('2. SVG文件显示了实际图标样式');
|
||||
console.log('3. 您可以使用在线工具将SVG转换为PNG');
|
||||
console.log('4. 或者打开generate-icons.html在浏览器中生成真实PNG图标');
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extEnable": true,
|
||||
"extAppid": "wx171c02d83197be01",
|
||||
"ext":{
|
||||
"appId":435
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>生成TabBar图标</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
}
|
||||
.icon-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.icon-item {
|
||||
border: 1px solid #ddd;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
.icon-item h3 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
canvas {
|
||||
border: 1px solid #eee;
|
||||
margin: 5px;
|
||||
}
|
||||
.download-btn {
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
padding: 5px 10px;
|
||||
background: #07c160;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.download-all {
|
||||
margin-top: 20px;
|
||||
padding: 10px 20px;
|
||||
background: #07c160;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
.instructions {
|
||||
background: #f0f8ff;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>打牌记账小程序 - TabBar图标生成器</h1>
|
||||
|
||||
<div class="instructions">
|
||||
<h3>使用说明:</h3>
|
||||
<ol>
|
||||
<li>点击每个图标下方的"下载"按钮保存单个图标</li>
|
||||
<li>或点击"下载所有图标"按钮一次性下载全部</li>
|
||||
<li>将下载的图片保存到 <code>miniapp/images/</code> 目录</li>
|
||||
<li>图标尺寸:81x81像素,适合小程序使用</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="icon-grid" id="iconGrid"></div>
|
||||
|
||||
<button class="download-all" onclick="downloadAll()">下载所有图标</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 图标配置
|
||||
const icons = [
|
||||
{ name: 'home', label: '首页图标(灰色)', color: '#999999' },
|
||||
{ name: 'home-active', label: '首页图标(绿色)', color: '#07c160' },
|
||||
{ name: 'stats', label: '统计图标(灰色)', color: '#999999' },
|
||||
{ name: 'stats-active', label: '统计图标(绿色)', color: '#07c160' },
|
||||
{ name: 'profile', label: '个人中心(灰色)', color: '#999999' },
|
||||
{ name: 'profile-active', label: '个人中心(绿色)', color: '#07c160' }
|
||||
];
|
||||
|
||||
// 绘制图标函数
|
||||
function drawIcon(ctx, type, color) {
|
||||
ctx.clearRect(0, 0, 81, 81);
|
||||
ctx.strokeStyle = color;
|
||||
ctx.fillStyle = color;
|
||||
ctx.lineWidth = 3;
|
||||
|
||||
switch(type) {
|
||||
case 'home':
|
||||
// 绘制房子图标
|
||||
ctx.beginPath();
|
||||
// 屋顶
|
||||
ctx.moveTo(40.5, 15);
|
||||
ctx.lineTo(20, 35);
|
||||
ctx.lineTo(61, 35);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
// 房子主体
|
||||
ctx.fillRect(25, 35, 31, 30);
|
||||
// 门
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(35, 50, 11, 15);
|
||||
break;
|
||||
|
||||
case 'stats':
|
||||
// 绘制统计图标(柱状图)
|
||||
ctx.fillRect(20, 45, 12, 20);
|
||||
ctx.fillRect(35, 35, 12, 30);
|
||||
ctx.fillRect(50, 25, 12, 40);
|
||||
break;
|
||||
|
||||
case 'profile':
|
||||
// 绘制个人图标
|
||||
// 头部
|
||||
ctx.beginPath();
|
||||
ctx.arc(40.5, 25, 10, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
// 身体
|
||||
ctx.beginPath();
|
||||
ctx.arc(40.5, 55, 18, Math.PI, 0, true);
|
||||
ctx.fill();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建图标
|
||||
function createIcons() {
|
||||
const container = document.getElementById('iconGrid');
|
||||
|
||||
icons.forEach(icon => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'icon-item';
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 81;
|
||||
canvas.height = 81;
|
||||
canvas.id = icon.name;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
const type = icon.name.replace('-active', '');
|
||||
drawIcon(ctx, type, icon.color);
|
||||
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = icon.label;
|
||||
|
||||
const downloadBtn = document.createElement('a');
|
||||
downloadBtn.className = 'download-btn';
|
||||
downloadBtn.textContent = '下载';
|
||||
downloadBtn.download = icon.name + '.png';
|
||||
downloadBtn.href = canvas.toDataURL('image/png');
|
||||
|
||||
div.appendChild(title);
|
||||
div.appendChild(canvas);
|
||||
div.appendChild(document.createElement('br'));
|
||||
div.appendChild(downloadBtn);
|
||||
|
||||
container.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
// 下载所有图标
|
||||
function downloadAll() {
|
||||
icons.forEach((icon, index) => {
|
||||
setTimeout(() => {
|
||||
const canvas = document.getElementById(icon.name);
|
||||
const link = document.createElement('a');
|
||||
link.download = icon.name + '.png';
|
||||
link.href = canvas.toDataURL('image/png');
|
||||
link.click();
|
||||
}, index * 200); // 延迟下载,避免浏览器阻止
|
||||
});
|
||||
}
|
||||
|
||||
// 页面加载完成后创建图标
|
||||
window.onload = createIcons;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 560 B |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="40.5" cy="25" r="12" fill="#cccccc"/>
|
||||
<ellipse cx="40.5" cy="55" rx="20" ry="15" fill="#cccccc"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 247 B |
|
After Width: | Height: | Size: 70 B |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M40.5 15 L20 35 L25 35 L25 65 L35 65 L35 50 L46 50 L46 65 L56 65 L56 35 L61 35 Z"
|
||||
fill="#dddddd" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 252 B |
|
After Width: | Height: | Size: 70 B |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M40.5 15 L20 35 L25 35 L25 65 L35 65 L35 50 L46 50 L46 65 L56 65 L56 35 L61 35 Z"
|
||||
fill="#07c160" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 252 B |
|
After Width: | Height: | Size: 70 B |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M40.5 15 L20 35 L25 35 L25 65 L35 65 L35 50 L46 50 L46 65 L56 65 L56 35 L61 35 Z"
|
||||
fill="#999999" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 252 B |
|
|
@ -0,0 +1,23 @@
|
|||
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- 圆形背景 -->
|
||||
<circle cx="100" cy="100" r="90" fill="#07c160"/>
|
||||
|
||||
<!-- 白色内圆 -->
|
||||
<circle cx="100" cy="100" r="75" fill="white"/>
|
||||
|
||||
<!-- 麻将图标 - 简化设计 -->
|
||||
<!-- 麻将牌外框 -->
|
||||
<rect x="60" y="50" width="80" height="100" rx="8" fill="#07c160" stroke="#fff" stroke-width="2"/>
|
||||
|
||||
<!-- 麻将牌内部装饰 -->
|
||||
<rect x="70" y="60" width="60" height="10" fill="white" opacity="0.9"/>
|
||||
<rect x="70" y="130" width="60" height="10" fill="white" opacity="0.9"/>
|
||||
|
||||
<!-- 中文"记账"字样区域(简化表示) -->
|
||||
<circle cx="80" cy="95" r="8" fill="white"/>
|
||||
<circle cx="100" cy="95" r="8" fill="white"/>
|
||||
<circle cx="120" cy="95" r="8" fill="white"/>
|
||||
|
||||
<circle cx="90" cy="110" r="6" fill="white"/>
|
||||
<circle cx="110" cy="110" r="6" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 873 B |
|
After Width: | Height: | Size: 70 B |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="40.5" cy="25" r="12" fill="#07c160"/>
|
||||
<ellipse cx="40.5" cy="55" rx="20" ry="15" fill="#07c160"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 247 B |
|
After Width: | Height: | Size: 70 B |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="40.5" cy="25" r="12" fill="#999999"/>
|
||||
<ellipse cx="40.5" cy="55" rx="20" ry="15" fill="#999999"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 247 B |
|
After Width: | Height: | Size: 70 B |
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="15" y="45" width="15" height="20" fill="#07c160"/>
|
||||
<rect x="33" y="35" width="15" height="30" fill="#07c160"/>
|
||||
<rect x="51" y="25" width="15" height="40" fill="#07c160"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 321 B |
|
After Width: | Height: | Size: 70 B |
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81" height="81" viewBox="0 0 81 81" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="15" y="45" width="15" height="20" fill="#999999"/>
|
||||
<rect x="33" y="35" width="15" height="30" fill="#999999"/>
|
||||
<rect x="51" y="25" width="15" height="40" fill="#999999"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 321 B |
|
|
@ -0,0 +1,295 @@
|
|||
// Generated by dts-bundle v0.7.3
|
||||
|
||||
declare module '@jdmini/api' {
|
||||
import { onLoginReady, waitLogin } from '@jdmini/api/app';
|
||||
import HttpClient, { gatewayHttpClient, baseHttpClient, apiHttpClient } from '@jdmini/api/httpClient';
|
||||
import { injectApp, injectPage, injectComponent, hijackApp, hijackAllPage } from '@jdmini/api/injector';
|
||||
import adManager from '@jdmini/api/adManager';
|
||||
export { onLoginReady, waitLogin, injectApp, injectPage, injectComponent, hijackApp, hijackAllPage, gatewayHttpClient, baseHttpClient, apiHttpClient, HttpClient, adManager, };
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/app' {
|
||||
export interface AppOptions {
|
||||
gatewayUrl?: string;
|
||||
baseUrl?: string;
|
||||
apiUrl?: string;
|
||||
}
|
||||
export interface PageOptions {
|
||||
showInterstitialAd?: boolean;
|
||||
}
|
||||
export function initApp(options?: AppOptions): Promise<void>;
|
||||
export function showPage(options: PageOptions | undefined, pageId: string): Promise<void>;
|
||||
export const checkTokenValid: () => boolean;
|
||||
/**
|
||||
* 确保登录完成
|
||||
* @param {Function} callback - 回调函数
|
||||
* @returns {void}
|
||||
*/
|
||||
export function onLoginReady(callback: (...args: any[]) => void): void;
|
||||
/**
|
||||
* 等待登录完成
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export function waitLogin(): Promise<void>;
|
||||
export function login(): Promise<void>;
|
||||
export function fetchEchoData(): Promise<void>;
|
||||
export function trackVisit(): Promise<void>;
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/httpClient' {
|
||||
import { HttpClientOptions, RequestOptions, ApiResponse } from '@jdmini/api/types';
|
||||
class HttpClient {
|
||||
constructor({ baseURL, timeout }: HttpClientOptions);
|
||||
setBaseURL(baseURL: string): void;
|
||||
/**
|
||||
* 请求
|
||||
* @param {string} path 路径
|
||||
* @param {string} method 方法, 默认GET
|
||||
* @param {Object} data 数据, 默认{}
|
||||
* @param {Object} options 传入wx.request的其他配置, 默认{}
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
request<T = any>(path: string, method?: WechatMiniprogram.RequestOption['method'], data?: Record<string, any>, options?: RequestOptions): Promise<ApiResponse<T>>;
|
||||
/**
|
||||
* 上传文件
|
||||
* @param {string} filePath 文件路径
|
||||
* @param {Object} data 数据, 默认{}
|
||||
* @param {'avatar' | 'file'} type 类型, 默认'file'
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
uploadFile<T = any>(filePath: string, data?: Record<string, any>, type?: 'avatar' | 'file'): Promise<ApiResponse<T>>;
|
||||
/**
|
||||
* 上传文件
|
||||
* @param {string} filePath 文件路径
|
||||
* @param {Object} data 数据, 默认{}
|
||||
* @param {'avatar' | 'file'} type 类型, 默认'file'
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
upload<T = any>(path: string, filePath: string, data?: Record<string, any>): Promise<ApiResponse<T>>;
|
||||
/**
|
||||
* 删除文件
|
||||
* @param {number} fileId 文件id
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
deleteFile(fileId: number): Promise<ApiResponse<null>>;
|
||||
/**
|
||||
* 上传头像
|
||||
* @param {string} filePath 文件路径
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
uploadAvatar<T = any>(filePath: string): Promise<ApiResponse<T>>;
|
||||
}
|
||||
export const gatewayHttpClient: HttpClient;
|
||||
export const baseHttpClient: HttpClient;
|
||||
export const apiHttpClient: HttpClient;
|
||||
export default HttpClient;
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/injector' {
|
||||
interface AppConfig {
|
||||
onLaunch?: (...args: any[]) => void | Promise<void>;
|
||||
[key: string]: any;
|
||||
}
|
||||
interface PageConfig {
|
||||
onShow?: (...args: any[]) => void | Promise<void>;
|
||||
[key: string]: any;
|
||||
}
|
||||
interface ComponentConfig {
|
||||
methods?: {
|
||||
onLoad?: (...args: any[]) => void | Promise<void>;
|
||||
onShow?: (...args: any[]) => void | Promise<void>;
|
||||
[key: string]: any;
|
||||
};
|
||||
[key: string]: any;
|
||||
}
|
||||
interface InjectAppOptions {
|
||||
gatewayUrl?: string;
|
||||
baseUrl?: string;
|
||||
apiUrl?: string;
|
||||
}
|
||||
interface InjectPageOptions {
|
||||
showInterstitialAd?: boolean;
|
||||
}
|
||||
/**
|
||||
* 注入应用配置
|
||||
* @param {Object} options - 配置选项
|
||||
* @param {string} [options.gatewayUrl] - 网关地址,默认使用CONFIG.API.GATEWAY_URL
|
||||
* @param {string} [options.baseUrl] - 基础地址,默认使用CONFIG.API.BASE_URL
|
||||
* @param {string} [options.apiUrl] - api地址,默认使用CONFIG.API.API_URL
|
||||
* @returns {Function} 返回一个接收应用配置的函数
|
||||
*/
|
||||
export function injectApp(options?: InjectAppOptions): (appConfig: AppConfig) => AppConfig;
|
||||
/**
|
||||
* 注入页面配置
|
||||
* @param {InjectPageOptions} options - 配置选项
|
||||
* @param {boolean} [options.showInterstitialAd] - 是否在onShow显示插屏广告,默认不显示
|
||||
* @returns {Function} 返回一个接收页面配置的函数
|
||||
*/
|
||||
export function injectPage(options?: InjectPageOptions): (pageConfig?: PageConfig) => PageConfig;
|
||||
/**
|
||||
* 注入组件配置
|
||||
* @param {InjectPageOptions} options - 配置选项
|
||||
* @param {boolean} [options.showInterstitialAd] - 是否在onShow显示插屏广告,默认不显示
|
||||
* @returns {Function} 返回一个接收组件配置的函数
|
||||
*/
|
||||
export function injectComponent(options?: InjectPageOptions): (pageConfig?: PageConfig) => ComponentConfig;
|
||||
/**
|
||||
* 劫持App
|
||||
* @param {InjectAppOptions} options - 配置选项
|
||||
* @param {string} [options.gatewayUrl] - 网关地址,默认使用CONFIG.API.GATEWAY_URL
|
||||
* @param {string} [options.baseUrl] - 基础地址,默认使用CONFIG.API.BASE_URL
|
||||
* @param {string} [options.apiUrl] - api地址,默认使用CONFIG.API.API_URL
|
||||
* @returns {void}
|
||||
*/
|
||||
export const hijackApp: (options?: InjectAppOptions) => void;
|
||||
/**
|
||||
* 劫持所有Page
|
||||
* @param {InjectPageOptions} options - 配置选项
|
||||
* @param {boolean} [options.showInterstitialAd] - 是否在onShow显示插屏广告,默认不显示
|
||||
* @returns {void}
|
||||
*/
|
||||
export const hijackAllPage: (options?: InjectPageOptions) => void;
|
||||
export {};
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/adManager' {
|
||||
import { AdData, LinkData, TopData } from '@jdmini/api/types';
|
||||
type Ads = Partial<Record<AdData['appPage'], AdData['ads'][0]['adUnitId']>>;
|
||||
class AdManager {
|
||||
/**
|
||||
* 广告数据
|
||||
*/
|
||||
ads: Ads;
|
||||
/**
|
||||
* 友情链接数据
|
||||
*/
|
||||
link: LinkData[];
|
||||
/**
|
||||
* 友情链接顶部广告数据
|
||||
*/
|
||||
top: TopData | null;
|
||||
constructor();
|
||||
/**
|
||||
* 确保广告数据就绪
|
||||
* @param {Function} callback - 回调函数
|
||||
* @returns {void}
|
||||
*/
|
||||
onDataReady: (callback: (...args: any[]) => void) => void;
|
||||
/**
|
||||
* 等待广告数据加载完成
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
waitAdData: () => Promise<void>;
|
||||
/**
|
||||
* 初始化广告数据
|
||||
* @returns {void}
|
||||
*/
|
||||
init: () => void;
|
||||
/**
|
||||
* 创建并展示插屏广告
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
createAndShowInterstitialAd: () => Promise<void>;
|
||||
/**
|
||||
* 创建并展示激励视频广告
|
||||
* @param {any} context - 页面上下文
|
||||
* @param {string} [pageId] - 页面ID
|
||||
* @returns {Promise<boolean>} 是否完成播放
|
||||
*/
|
||||
createAndShowRewardedVideoAd: (context: any, pageId?: string) => Promise<boolean>;
|
||||
}
|
||||
const _default: AdManager;
|
||||
export default _default;
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/types' {
|
||||
export interface Config {
|
||||
API: {
|
||||
GATEWAY_URL: string;
|
||||
BASE_URL: string;
|
||||
API_URL: string;
|
||||
};
|
||||
APP: {
|
||||
APP_ID: number;
|
||||
LOGIN_MAX_RETRY: number;
|
||||
};
|
||||
HTTP: {
|
||||
TIMEOUT: number;
|
||||
};
|
||||
DATA: {
|
||||
PAGE_ID: string;
|
||||
};
|
||||
STORAGE_KEYS: {
|
||||
TOKEN: string;
|
||||
USER_INFO: string;
|
||||
SPA_DATA: string;
|
||||
LINK_DATA: string;
|
||||
TOP_DATA: string;
|
||||
};
|
||||
EVENT_KEYS: {
|
||||
LOGIN_SUCCESS: string;
|
||||
AD_DATA_READY: string;
|
||||
REWARDED_VIDEO_AD_CLOSE: string;
|
||||
};
|
||||
}
|
||||
export interface HttpClientOptions {
|
||||
baseURL: string;
|
||||
timeout?: number;
|
||||
}
|
||||
export interface RequestOptions {
|
||||
headers?: Record<string, string>;
|
||||
[key: string]: any;
|
||||
}
|
||||
export interface LoginData {
|
||||
appId: number;
|
||||
code: string;
|
||||
brand: string;
|
||||
model: string;
|
||||
platform: string;
|
||||
}
|
||||
export interface ApiResponse<T = any> {
|
||||
code: number;
|
||||
message?: string;
|
||||
data?: T;
|
||||
}
|
||||
export interface UserInfo {
|
||||
token: string;
|
||||
user: {
|
||||
id: number;
|
||||
name: string;
|
||||
avatar: string;
|
||||
openId: string;
|
||||
};
|
||||
}
|
||||
export interface EchoData {
|
||||
isPublished: boolean;
|
||||
spads: AdData[];
|
||||
links: LinkData[];
|
||||
top: TopData;
|
||||
version: number;
|
||||
}
|
||||
export interface AdData {
|
||||
id: number;
|
||||
status: number;
|
||||
appPage: 'banner' | 'custom' | 'video' | 'interstitial' | 'rewarded';
|
||||
ads: {
|
||||
id: number;
|
||||
type: number;
|
||||
adId: number;
|
||||
adUnitId: string;
|
||||
}[];
|
||||
}
|
||||
export interface LinkData {
|
||||
appId: string;
|
||||
appLogo: string;
|
||||
linkName: string;
|
||||
linkPage: string;
|
||||
}
|
||||
export interface TopData {
|
||||
appId: string;
|
||||
appLogo: string;
|
||||
linkName: string;
|
||||
appDsc: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
import { adManager } from '@jdmini/api'
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
type: {
|
||||
type: String,
|
||||
value: 'custom' // 可选banner, video, custom
|
||||
}
|
||||
},
|
||||
data: {
|
||||
ads: {}
|
||||
},
|
||||
lifetimes: {
|
||||
attached: function () {
|
||||
adManager.onDataReady(() => {
|
||||
this.setData({ ads: adManager.ads })
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<view class="jdwx-ad-component">
|
||||
<ad wx:if="{{type === 'banner' && ads.banner}}" class="jdwx-ad-item" unit-id="{{ads.banner}}"></ad>
|
||||
<ad wx:if="{{type === 'video' && ads.video}}" class="jdwx-ad-item" ad-type="video" unit-id="{{ads.video}}"></ad>
|
||||
<ad-custom wx:if="{{type === 'custom' && ads.custom}}" class="jdwx-ad-item" unit-id="{{ads.custom}}"></ad-custom>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
.jdwx-ad-component {
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
.jdwx-ad-item {
|
||||
bottom: 10rpx;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { adManager } from '@jdmini/api'
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
},
|
||||
data: {
|
||||
link: [],
|
||||
top: {},
|
||||
},
|
||||
pageLifetimes: {
|
||||
show: function () {
|
||||
adManager.onDataReady(() => {
|
||||
this.setData({
|
||||
link: adManager.link,
|
||||
top: adManager.top
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
gotopLink: function () {
|
||||
wx.vibrateShort()
|
||||
wx.openEmbeddedMiniProgram({
|
||||
appId: this.data.top.appId,
|
||||
path: this.data.top.linkPage
|
||||
});
|
||||
},
|
||||
goLink: function (e) {
|
||||
let index = e.currentTarget.id
|
||||
wx.vibrateShort()
|
||||
wx.openEmbeddedMiniProgram({
|
||||
appId: this.data.link[index].appId,
|
||||
path: this.data.link[index].linkPage
|
||||
});
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<view class="jdwx-link-component">
|
||||
<view wx:if="{{top.appId}}" class="jdwx-applink-top" bindtap="gotopLink">
|
||||
<view><image src="{{top.appLogo}}" class="jdwx-applink-icon" /> </view>
|
||||
<view ><text class="jdwx-applink-top-linkname">{{top.linkName}}</text>
|
||||
<text class="jdwx-applink-top-text">{{top.appDsc}}</text> </view>
|
||||
</view>
|
||||
<view id="{{bindex}}" bindtap="goLink" wx:for="{{link}}" wx:for-index="bindex" wx:key="index" class="jdwx-applink-list">
|
||||
<image src="{{item.appLogo}}" class="jdwx-applink-icon" />
|
||||
<text class="jdwx-applink-text">{{item.linkName}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/* 页面容器 */
|
||||
.jdwx-link-component {
|
||||
/* background-image: linear-gradient(to right, #4F9863, #4F9863); */
|
||||
background-attachment: fixed;
|
||||
background-size: cover; /* 确保背景图像覆盖整个元素 */
|
||||
/* height: 100vh; */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
overflow: auto; /* 允许内容滚动 */
|
||||
}
|
||||
|
||||
/* 列表项样式 */
|
||||
.jdwx-applink-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 95%;
|
||||
/* 假设我们想要每个view的高度约为屏幕高度的1/8 */
|
||||
/* 使用小程序的wx.getSystemInfo API动态计算并设置这个值会更准确 */
|
||||
height: calc((100vh - 40rpx) / 10); /* 减去容器padding的影响 */
|
||||
padding: 20rpx;
|
||||
background-color: rgba(248, 250, 252, 1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.jdwx-applink-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 95%;
|
||||
/* 假设我们想要每个view的高度约为屏幕高度的1/8 */
|
||||
/* 使用小程序的wx.getSystemInfo API动态计算并设置这个值会更准确 */
|
||||
height: calc((100vh - 40rpx) / 6); /* 减去容器padding的影响 */
|
||||
padding: 20rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 30rpx;
|
||||
background-color: rgba(248, 250, 252, 1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.jdwx-applink-top-linkname{
|
||||
display: flex;
|
||||
font-size: 36rpx;
|
||||
color: rgb(39, 37, 37);
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
/* 图标样式 */
|
||||
.jdwx-applink-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 50rpx;
|
||||
margin-left: 30rpx;
|
||||
}
|
||||
|
||||
/* 文本样式 */
|
||||
.jdwx-applink-text {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: rgb(39, 37, 37);
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1761629501967, function(require, module, exports) {
|
||||
|
||||
|
||||
var hasOwn = Object.prototype.hasOwnProperty;
|
||||
var toStr = Object.prototype.toString;
|
||||
var defineProperty = Object.defineProperty;
|
||||
var gOPD = Object.getOwnPropertyDescriptor;
|
||||
|
||||
var isArray = function isArray(arr) {
|
||||
if (typeof Array.isArray === 'function') {
|
||||
return Array.isArray(arr);
|
||||
}
|
||||
|
||||
return toStr.call(arr) === '[object Array]';
|
||||
};
|
||||
|
||||
var isPlainObject = function isPlainObject(obj) {
|
||||
if (!obj || toStr.call(obj) !== '[object Object]') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var hasOwnConstructor = hasOwn.call(obj, 'constructor');
|
||||
var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
|
||||
// Not own constructor property must be Object
|
||||
if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Own properties are enumerated firstly, so to speed up,
|
||||
// if last one is own, then all properties are own.
|
||||
var key;
|
||||
for (key in obj) { /**/ }
|
||||
|
||||
return typeof key === 'undefined' || hasOwn.call(obj, key);
|
||||
};
|
||||
|
||||
// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target
|
||||
var setProperty = function setProperty(target, options) {
|
||||
if (defineProperty && options.name === '__proto__') {
|
||||
defineProperty(target, options.name, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
value: options.newValue,
|
||||
writable: true
|
||||
});
|
||||
} else {
|
||||
target[options.name] = options.newValue;
|
||||
}
|
||||
};
|
||||
|
||||
// Return undefined instead of __proto__ if '__proto__' is not an own property
|
||||
var getProperty = function getProperty(obj, name) {
|
||||
if (name === '__proto__') {
|
||||
if (!hasOwn.call(obj, name)) {
|
||||
return void 0;
|
||||
} else if (gOPD) {
|
||||
// In early versions of node, obj['__proto__'] is buggy when obj has
|
||||
// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.
|
||||
return gOPD(obj, name).value;
|
||||
}
|
||||
}
|
||||
|
||||
return obj[name];
|
||||
};
|
||||
|
||||
module.exports = function extend() {
|
||||
var options, name, src, copy, copyIsArray, clone;
|
||||
var target = arguments[0];
|
||||
var i = 1;
|
||||
var length = arguments.length;
|
||||
var deep = false;
|
||||
|
||||
// Handle a deep copy situation
|
||||
if (typeof target === 'boolean') {
|
||||
deep = target;
|
||||
target = arguments[1] || {};
|
||||
// skip the boolean and the target
|
||||
i = 2;
|
||||
}
|
||||
if (target == null || (typeof target !== 'object' && typeof target !== 'function')) {
|
||||
target = {};
|
||||
}
|
||||
|
||||
for (; i < length; ++i) {
|
||||
options = arguments[i];
|
||||
// Only deal with non-null/undefined values
|
||||
if (options != null) {
|
||||
// Extend the base object
|
||||
for (name in options) {
|
||||
src = getProperty(target, name);
|
||||
copy = getProperty(options, name);
|
||||
|
||||
// Prevent never-ending loop
|
||||
if (target !== copy) {
|
||||
// Recurse if we're merging plain objects or arrays
|
||||
if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
|
||||
if (copyIsArray) {
|
||||
copyIsArray = false;
|
||||
clone = src && isArray(src) ? src : [];
|
||||
} else {
|
||||
clone = src && isPlainObject(src) ? src : {};
|
||||
}
|
||||
|
||||
// Never move original objects, clone them
|
||||
setProperty(target, { name: name, newValue: extend(deep, clone, copy) });
|
||||
|
||||
// Don't bring in undefined values
|
||||
} else if (typeof copy !== 'undefined') {
|
||||
setProperty(target, { name: name, newValue: copy });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the modified object
|
||||
return target;
|
||||
};
|
||||
|
||||
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1761629501967);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=[]
|
||||
//# sourceMappingURL=index.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["\n\nvar hasOwn = Object.prototype.hasOwnProperty;\nvar toStr = Object.prototype.toString;\nvar defineProperty = Object.defineProperty;\nvar gOPD = Object.getOwnPropertyDescriptor;\n\nvar isArray = function isArray(arr) {\n\tif (typeof Array.isArray === 'function') {\n\t\treturn Array.isArray(arr);\n\t}\n\n\treturn toStr.call(arr) === '[object Array]';\n};\n\nvar isPlainObject = function isPlainObject(obj) {\n\tif (!obj || toStr.call(obj) !== '[object Object]') {\n\t\treturn false;\n\t}\n\n\tvar hasOwnConstructor = hasOwn.call(obj, 'constructor');\n\tvar hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');\n\t// Not own constructor property must be Object\n\tif (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {\n\t\treturn false;\n\t}\n\n\t// Own properties are enumerated firstly, so to speed up,\n\t// if last one is own, then all properties are own.\n\tvar key;\n\tfor (key in obj) { /**/ }\n\n\treturn typeof key === 'undefined' || hasOwn.call(obj, key);\n};\n\n// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target\nvar setProperty = function setProperty(target, options) {\n\tif (defineProperty && options.name === '__proto__') {\n\t\tdefineProperty(target, options.name, {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\t\t\tvalue: options.newValue,\n\t\t\twritable: true\n\t\t});\n\t} else {\n\t\ttarget[options.name] = options.newValue;\n\t}\n};\n\n// Return undefined instead of __proto__ if '__proto__' is not an own property\nvar getProperty = function getProperty(obj, name) {\n\tif (name === '__proto__') {\n\t\tif (!hasOwn.call(obj, name)) {\n\t\t\treturn void 0;\n\t\t} else if (gOPD) {\n\t\t\t// In early versions of node, obj['__proto__'] is buggy when obj has\n\t\t\t// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.\n\t\t\treturn gOPD(obj, name).value;\n\t\t}\n\t}\n\n\treturn obj[name];\n};\n\nmodule.exports = function extend() {\n\tvar options, name, src, copy, copyIsArray, clone;\n\tvar target = arguments[0];\n\tvar i = 1;\n\tvar length = arguments.length;\n\tvar deep = false;\n\n\t// Handle a deep copy situation\n\tif (typeof target === 'boolean') {\n\t\tdeep = target;\n\t\ttarget = arguments[1] || {};\n\t\t// skip the boolean and the target\n\t\ti = 2;\n\t}\n\tif (target == null || (typeof target !== 'object' && typeof target !== 'function')) {\n\t\ttarget = {};\n\t}\n\n\tfor (; i < length; ++i) {\n\t\toptions = arguments[i];\n\t\t// Only deal with non-null/undefined values\n\t\tif (options != null) {\n\t\t\t// Extend the base object\n\t\t\tfor (name in options) {\n\t\t\t\tsrc = getProperty(target, name);\n\t\t\t\tcopy = getProperty(options, name);\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif (target !== copy) {\n\t\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\t\tif (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {\n\t\t\t\t\t\tif (copyIsArray) {\n\t\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\t\tclone = src && isArray(src) ? src : [];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tclone = src && isPlainObject(src) ? src : {};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\t\tsetProperty(target, { name: name, newValue: extend(deep, clone, copy) });\n\n\t\t\t\t\t// Don't bring in undefined values\n\t\t\t\t\t} else if (typeof copy !== 'undefined') {\n\t\t\t\t\t\tsetProperty(target, { name: name, newValue: copy });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n"]}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "miniapp",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/@jdmini/api": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmmirror.com/@jdmini/api/-/api-1.0.10.tgz",
|
||||
"integrity": "sha512-bVFU0awuY033mUT4QqArrYbrnPkBaBFKHoqCMHTVnRCk4b6gTs+cCGDH8uyf2t8ybCgWITKxaaH4Vjzyq8VF8g=="
|
||||
},
|
||||
"node_modules/@jdmini/components": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@jdmini/components/-/components-1.0.6.tgz",
|
||||
"integrity": "sha512-ndva1nlZ1QJqDVgHfB0GPxMGmXsZ7SbWjUkRm/WoQIkow75fFbaQCW/xhtQQ+bPbJLjXmCg2p2356klsLLib8A==",
|
||||
"peerDependencies": {
|
||||
"@jdmini/api": ">=1.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"node_modules/weapp-qrcode": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/weapp-qrcode/-/weapp-qrcode-1.0.0.tgz",
|
||||
"integrity": "sha512-4sa3W0rGDVJ9QaeZpAKlAuUxVyjhDwiUqHyGK/jJMsRMXnhb4yO8qWU/pZruMo+iT5J6CraS67lDMFb1VY+RaA==",
|
||||
"dependencies": {
|
||||
"extend": "^3.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
## 安装/更新
|
||||
|
||||
1、终端使用命令安装 npm 包
|
||||
|
||||
```bash
|
||||
npm install --save @jdmini/api@latest
|
||||
```
|
||||
|
||||
> ```bash
|
||||
> npm install --save @jdmini/components@latest
|
||||
> ```
|
||||
|
||||
2、在小程序开发者工具中:菜单选择工具 -> 构建 npm
|
||||
|
||||
## 使用
|
||||
|
||||
```js
|
||||
import {
|
||||
onLoginReady,
|
||||
waitLogin,
|
||||
|
||||
injectApp,
|
||||
injectPage,
|
||||
injectComponent,
|
||||
hijackApp,
|
||||
hijackAllPage,
|
||||
|
||||
gatewayHttpClient,
|
||||
baseHttpClient,
|
||||
apiHttpClient,
|
||||
HttpClient,
|
||||
|
||||
adManager,
|
||||
} from '@jdmini/api'
|
||||
```
|
||||
|
||||
### `waitLogin`/`onLoginReady` - 确保登录完成
|
||||
|
||||
- 同步的写法
|
||||
|
||||
```js
|
||||
async function onLoad() {
|
||||
await waitLogin()
|
||||
// 此处代码将在已登录或登陆完成后执行。请求将自动携带Token
|
||||
await gatewayHttpClient.request('/xxx', 'GET', {})
|
||||
}
|
||||
```
|
||||
|
||||
- 异步的写法
|
||||
|
||||
```js
|
||||
function onLoad() {
|
||||
onLoginReady(() => {
|
||||
// 此处代码将在已登录或登陆完成后执行。请求将自动携带Token
|
||||
gatewayHttpClient.request('/xxx', 'GET', {})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### `injectApp` - 向App注入基础代码
|
||||
|
||||
- 注入之后实现自动登录、广告初始化等功能
|
||||
|
||||
```js
|
||||
// app.js
|
||||
App(injectApp()({
|
||||
// 业务代码
|
||||
onLaunch() {
|
||||
|
||||
}
|
||||
}))
|
||||
```
|
||||
|
||||
### `injectPage` - 向Page注入基础代码
|
||||
|
||||
- 注入之后实现页面自动统计、自动展示插屏广告以及激励视频广告的调用支持
|
||||
- 参数:
|
||||
- showInterstitialAd: 是否自动展示插屏广告
|
||||
|
||||
```js
|
||||
// pages/xxx/xxx.js
|
||||
Page(injectPage({
|
||||
showInterstitialAd: true
|
||||
})({
|
||||
// 业务代码
|
||||
onLoad() {
|
||||
|
||||
}
|
||||
}))
|
||||
```
|
||||
|
||||
### `injectComponent` - 向Component注入基础代码
|
||||
|
||||
- 适用于使用Component构造页面的场景
|
||||
- 注入之后实现页面自动统计、自动展示插屏广告以及激励视频广告的调用支持
|
||||
- 参数:
|
||||
- showInterstitialAd: 是否自动展示插屏广告
|
||||
|
||||
```js
|
||||
// pages/xxx/xxx.js
|
||||
Component(injectComponent({
|
||||
showInterstitialAd: true
|
||||
})({
|
||||
// 业务代码
|
||||
methods: {
|
||||
onLoad() {
|
||||
|
||||
}
|
||||
}
|
||||
}))
|
||||
```
|
||||
|
||||
### `hijackApp` - 劫持全局App方法,注入基础代码
|
||||
|
||||
- 在不方便使用injectApp时使用(如解包后代码复杂,难以修改App调用)
|
||||
- 此方法会修改全局App方法,存在未知风险,使用时请进行完整测试
|
||||
- 不可与injectApp同时使用
|
||||
|
||||
```js
|
||||
// app.js
|
||||
hijackApp()
|
||||
```
|
||||
|
||||
### `hijackAllPage` - 劫持全局Page方法,注入基础代码
|
||||
|
||||
- 在不方便使用injectPage/injectComponent时使用(如解包后代码复杂,难以修改Page/Component调用)
|
||||
- 此方法会修改全局Page方法,并影响所有的页面,存在未知风险,使用时请进行完整测试
|
||||
- 参数同injectPage/injectComponent方法,不可与这些方法同时使用
|
||||
|
||||
```js
|
||||
// app.js
|
||||
hijackAllPage({
|
||||
showInterstitialAd: true
|
||||
})
|
||||
```
|
||||
|
||||
### `gatewayHttpClient` - 网关API调用封装
|
||||
|
||||
- 同步的写法
|
||||
|
||||
```js
|
||||
async function onLoad() {
|
||||
try {
|
||||
// 网关请求。参数:路径、方法、数据、其他选项(如headers、responseType)
|
||||
const data = await gatewayHttpClient.request(path, method, data,options)
|
||||
|
||||
// 头像上传。参数:文件路径
|
||||
const data = await gatewayHttpClient.uploadAvatar(filePath)
|
||||
|
||||
// 文件上传。参数:文件路径、数据
|
||||
const data = await gatewayHttpClient.uploadFile(filePath, data)
|
||||
|
||||
// 文件删除。参数:文件ID
|
||||
const data = await gatewayHttpClient.deleteFile(fileId)
|
||||
} catch(err) {
|
||||
// 响应HTTP状态码非200时自动showToast并抛出异常
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 所有方法均支持异步的写法
|
||||
|
||||
```js
|
||||
function onLoad() {
|
||||
gatewayHttpClient.request('/xxx')
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
})
|
||||
.catch(err => {})
|
||||
}
|
||||
```
|
||||
|
||||
### `baseHttpClient`/`apiHttpClient` - 为老版本兼容保留,不推荐使用
|
||||
|
||||
### `HttpClient` - API底层类,用于封装自定义请求
|
||||
|
||||
- 示例:封装一个百度的请求客户端,并调用百度搜索
|
||||
|
||||
```js
|
||||
const baiduHttpClient = new HttpClient({
|
||||
baseURL: 'https://www.baidu.com',
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
baiduHttpClient.request('/s', 'GET', { wd: '测试' }, { responseType: 'text' })
|
||||
.then(data => console.log(data))
|
||||
```
|
||||
|
||||
### `adManager` - 广告管理器
|
||||
|
||||
- 确保广告数据加载完成,支持同步/异步的写法
|
||||
|
||||
```js
|
||||
// 同步的写法
|
||||
async function onLoad() {
|
||||
await adManager.waitAdData()
|
||||
// 此处代码将在广告数据加载完成后执行
|
||||
await adManager.createAndShowInterstitialAd()
|
||||
}
|
||||
|
||||
// 异步的写法
|
||||
function onLoad () {
|
||||
adManager.onDataReady(() => {
|
||||
// 此处代码将在广告数据加载完成后执行
|
||||
adManager.createAndShowInterstitialAd()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
- 广告数据
|
||||
|
||||
```js
|
||||
// 格式化之后的广告数据对象,如{banner: "adunit-f7709f349de05edc", custom: "adunit-34c76b0c3e4a6ed0", ...}
|
||||
const ads = adManager.ads
|
||||
|
||||
// 友情链接顶部广告数据
|
||||
const top = adManager.top
|
||||
|
||||
// 友情链接数据
|
||||
const link = adManager.link
|
||||
```
|
||||
|
||||
- 创建并展示插屏广告
|
||||
|
||||
```js
|
||||
function onLoad() {
|
||||
adManager.createAndShowInterstitialAd()
|
||||
}
|
||||
```
|
||||
|
||||
- 创建并展示激励视频广告
|
||||
- 传入当前页面的上下文this,返回用户是否已看完广告
|
||||
- 由于微信的底层限制,需要先在调用的页面上进行injectPage注入,且该方法必须放在用户的点击事件里调用
|
||||
- 使用示例可参考[jdwx-demo](https://code.miniappapi.com/wx/jdwx-demo)
|
||||
|
||||
```js
|
||||
// 同步的写法
|
||||
page({
|
||||
async handleClick() {
|
||||
const isEnded = await adManager.createAndShowRewardedVideoAd(this)
|
||||
}
|
||||
})
|
||||
|
||||
// 异步的写法
|
||||
page({
|
||||
handleClick() {
|
||||
adManager.createAndShowRewardedVideoAd(this).then((isEnded) => {
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
// Generated by dts-bundle v0.7.3
|
||||
|
||||
declare module '@jdmini/api' {
|
||||
import { onLoginReady, waitLogin } from '@jdmini/api/app';
|
||||
import HttpClient, { gatewayHttpClient, baseHttpClient, apiHttpClient } from '@jdmini/api/httpClient';
|
||||
import { injectApp, injectPage, injectComponent, hijackApp, hijackAllPage } from '@jdmini/api/injector';
|
||||
import adManager from '@jdmini/api/adManager';
|
||||
export { onLoginReady, waitLogin, injectApp, injectPage, injectComponent, hijackApp, hijackAllPage, gatewayHttpClient, baseHttpClient, apiHttpClient, HttpClient, adManager, };
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/app' {
|
||||
export interface AppOptions {
|
||||
gatewayUrl?: string;
|
||||
baseUrl?: string;
|
||||
apiUrl?: string;
|
||||
}
|
||||
export interface PageOptions {
|
||||
showInterstitialAd?: boolean;
|
||||
}
|
||||
export function initApp(options?: AppOptions): Promise<void>;
|
||||
export function showPage(options: PageOptions | undefined, pageId: string): Promise<void>;
|
||||
export const checkTokenValid: () => boolean;
|
||||
/**
|
||||
* 确保登录完成
|
||||
* @param {Function} callback - 回调函数
|
||||
* @returns {void}
|
||||
*/
|
||||
export function onLoginReady(callback: (...args: any[]) => void): void;
|
||||
/**
|
||||
* 等待登录完成
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export function waitLogin(): Promise<void>;
|
||||
export function login(): Promise<void>;
|
||||
export function fetchEchoData(): Promise<void>;
|
||||
export function trackVisit(): Promise<void>;
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/httpClient' {
|
||||
import { HttpClientOptions, RequestOptions, ApiResponse } from '@jdmini/api/types';
|
||||
class HttpClient {
|
||||
constructor({ baseURL, timeout }: HttpClientOptions);
|
||||
setBaseURL(baseURL: string): void;
|
||||
/**
|
||||
* 请求
|
||||
* @param {string} path 路径
|
||||
* @param {string} method 方法, 默认GET
|
||||
* @param {Object} data 数据, 默认{}
|
||||
* @param {Object} options 传入wx.request的其他配置, 默认{}
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
request<T = any>(path: string, method?: WechatMiniprogram.RequestOption['method'], data?: Record<string, any>, options?: RequestOptions): Promise<ApiResponse<T>>;
|
||||
/**
|
||||
* 上传文件
|
||||
* @param {string} filePath 文件路径
|
||||
* @param {Object} data 数据, 默认{}
|
||||
* @param {'avatar' | 'file'} type 类型, 默认'file'
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
uploadFile<T = any>(filePath: string, data?: Record<string, any>, type?: 'avatar' | 'file'): Promise<ApiResponse<T>>;
|
||||
/**
|
||||
* 上传文件
|
||||
* @param {string} filePath 文件路径
|
||||
* @param {Object} data 数据, 默认{}
|
||||
* @param {'avatar' | 'file'} type 类型, 默认'file'
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
upload<T = any>(path: string, filePath: string, data?: Record<string, any>): Promise<ApiResponse<T>>;
|
||||
/**
|
||||
* 删除文件
|
||||
* @param {number} fileId 文件id
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
deleteFile(fileId: number): Promise<ApiResponse<null>>;
|
||||
/**
|
||||
* 上传头像
|
||||
* @param {string} filePath 文件路径
|
||||
* @returns {Promise<Object>} 返回一个Promise对象
|
||||
*/
|
||||
uploadAvatar<T = any>(filePath: string): Promise<ApiResponse<T>>;
|
||||
}
|
||||
export const gatewayHttpClient: HttpClient;
|
||||
export const baseHttpClient: HttpClient;
|
||||
export const apiHttpClient: HttpClient;
|
||||
export default HttpClient;
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/injector' {
|
||||
interface AppConfig {
|
||||
onLaunch?: (...args: any[]) => void | Promise<void>;
|
||||
[key: string]: any;
|
||||
}
|
||||
interface PageConfig {
|
||||
onShow?: (...args: any[]) => void | Promise<void>;
|
||||
[key: string]: any;
|
||||
}
|
||||
interface ComponentConfig {
|
||||
methods?: {
|
||||
onLoad?: (...args: any[]) => void | Promise<void>;
|
||||
onShow?: (...args: any[]) => void | Promise<void>;
|
||||
[key: string]: any;
|
||||
};
|
||||
[key: string]: any;
|
||||
}
|
||||
interface InjectAppOptions {
|
||||
gatewayUrl?: string;
|
||||
baseUrl?: string;
|
||||
apiUrl?: string;
|
||||
}
|
||||
interface InjectPageOptions {
|
||||
showInterstitialAd?: boolean;
|
||||
}
|
||||
/**
|
||||
* 注入应用配置
|
||||
* @param {Object} options - 配置选项
|
||||
* @param {string} [options.gatewayUrl] - 网关地址,默认使用CONFIG.API.GATEWAY_URL
|
||||
* @param {string} [options.baseUrl] - 基础地址,默认使用CONFIG.API.BASE_URL
|
||||
* @param {string} [options.apiUrl] - api地址,默认使用CONFIG.API.API_URL
|
||||
* @returns {Function} 返回一个接收应用配置的函数
|
||||
*/
|
||||
export function injectApp(options?: InjectAppOptions): (appConfig: AppConfig) => AppConfig;
|
||||
/**
|
||||
* 注入页面配置
|
||||
* @param {InjectPageOptions} options - 配置选项
|
||||
* @param {boolean} [options.showInterstitialAd] - 是否在onShow显示插屏广告,默认不显示
|
||||
* @returns {Function} 返回一个接收页面配置的函数
|
||||
*/
|
||||
export function injectPage(options?: InjectPageOptions): (pageConfig?: PageConfig) => PageConfig;
|
||||
/**
|
||||
* 注入组件配置
|
||||
* @param {InjectPageOptions} options - 配置选项
|
||||
* @param {boolean} [options.showInterstitialAd] - 是否在onShow显示插屏广告,默认不显示
|
||||
* @returns {Function} 返回一个接收组件配置的函数
|
||||
*/
|
||||
export function injectComponent(options?: InjectPageOptions): (pageConfig?: PageConfig) => ComponentConfig;
|
||||
/**
|
||||
* 劫持App
|
||||
* @param {InjectAppOptions} options - 配置选项
|
||||
* @param {string} [options.gatewayUrl] - 网关地址,默认使用CONFIG.API.GATEWAY_URL
|
||||
* @param {string} [options.baseUrl] - 基础地址,默认使用CONFIG.API.BASE_URL
|
||||
* @param {string} [options.apiUrl] - api地址,默认使用CONFIG.API.API_URL
|
||||
* @returns {void}
|
||||
*/
|
||||
export const hijackApp: (options?: InjectAppOptions) => void;
|
||||
/**
|
||||
* 劫持所有Page
|
||||
* @param {InjectPageOptions} options - 配置选项
|
||||
* @param {boolean} [options.showInterstitialAd] - 是否在onShow显示插屏广告,默认不显示
|
||||
* @returns {void}
|
||||
*/
|
||||
export const hijackAllPage: (options?: InjectPageOptions) => void;
|
||||
export {};
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/adManager' {
|
||||
import { AdData, LinkData, TopData } from '@jdmini/api/types';
|
||||
type Ads = Partial<Record<AdData['appPage'], AdData['ads'][0]['adUnitId']>>;
|
||||
class AdManager {
|
||||
/**
|
||||
* 广告数据
|
||||
*/
|
||||
ads: Ads;
|
||||
/**
|
||||
* 友情链接数据
|
||||
*/
|
||||
link: LinkData[];
|
||||
/**
|
||||
* 友情链接顶部广告数据
|
||||
*/
|
||||
top: TopData | null;
|
||||
constructor();
|
||||
/**
|
||||
* 确保广告数据就绪
|
||||
* @param {Function} callback - 回调函数
|
||||
* @returns {void}
|
||||
*/
|
||||
onDataReady: (callback: (...args: any[]) => void) => void;
|
||||
/**
|
||||
* 等待广告数据加载完成
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
waitAdData: () => Promise<void>;
|
||||
/**
|
||||
* 初始化广告数据
|
||||
* @returns {void}
|
||||
*/
|
||||
init: () => void;
|
||||
/**
|
||||
* 创建并展示插屏广告
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
createAndShowInterstitialAd: () => Promise<void>;
|
||||
/**
|
||||
* 创建并展示激励视频广告
|
||||
* @param {any} context - 页面上下文
|
||||
* @param {string} [pageId] - 页面ID
|
||||
* @returns {Promise<boolean>} 是否完成播放
|
||||
*/
|
||||
createAndShowRewardedVideoAd: (context: any, pageId?: string) => Promise<boolean>;
|
||||
}
|
||||
const _default: AdManager;
|
||||
export default _default;
|
||||
}
|
||||
|
||||
declare module '@jdmini/api/types' {
|
||||
export interface Config {
|
||||
API: {
|
||||
GATEWAY_URL: string;
|
||||
BASE_URL: string;
|
||||
API_URL: string;
|
||||
};
|
||||
APP: {
|
||||
APP_ID: number;
|
||||
LOGIN_MAX_RETRY: number;
|
||||
};
|
||||
HTTP: {
|
||||
TIMEOUT: number;
|
||||
};
|
||||
DATA: {
|
||||
PAGE_ID: string;
|
||||
};
|
||||
STORAGE_KEYS: {
|
||||
TOKEN: string;
|
||||
USER_INFO: string;
|
||||
SPA_DATA: string;
|
||||
LINK_DATA: string;
|
||||
TOP_DATA: string;
|
||||
};
|
||||
EVENT_KEYS: {
|
||||
LOGIN_SUCCESS: string;
|
||||
AD_DATA_READY: string;
|
||||
REWARDED_VIDEO_AD_CLOSE: string;
|
||||
};
|
||||
}
|
||||
export interface HttpClientOptions {
|
||||
baseURL: string;
|
||||
timeout?: number;
|
||||
}
|
||||
export interface RequestOptions {
|
||||
headers?: Record<string, string>;
|
||||
[key: string]: any;
|
||||
}
|
||||
export interface LoginData {
|
||||
appId: number;
|
||||
code: string;
|
||||
brand: string;
|
||||
model: string;
|
||||
platform: string;
|
||||
}
|
||||
export interface ApiResponse<T = any> {
|
||||
code: number;
|
||||
message?: string;
|
||||
data?: T;
|
||||
}
|
||||
export interface UserInfo {
|
||||
token: string;
|
||||
user: {
|
||||
id: number;
|
||||
name: string;
|
||||
avatar: string;
|
||||
openId: string;
|
||||
};
|
||||
}
|
||||
export interface EchoData {
|
||||
isPublished: boolean;
|
||||
spads: AdData[];
|
||||
links: LinkData[];
|
||||
top: TopData;
|
||||
version: number;
|
||||
}
|
||||
export interface AdData {
|
||||
id: number;
|
||||
status: number;
|
||||
appPage: 'banner' | 'custom' | 'video' | 'interstitial' | 'rewarded';
|
||||
ads: {
|
||||
id: number;
|
||||
type: number;
|
||||
adId: number;
|
||||
adUnitId: string;
|
||||
}[];
|
||||
}
|
||||
export interface LinkData {
|
||||
appId: string;
|
||||
appLogo: string;
|
||||
linkName: string;
|
||||
linkPage: string;
|
||||
}
|
||||
export interface TopData {
|
||||
appId: string;
|
||||
appLogo: string;
|
||||
linkName: string;
|
||||
appDsc: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "@jdmini/api",
|
||||
"version": "1.0.10",
|
||||
"main": "miniprogram_dist/index.js",
|
||||
"files": [
|
||||
"miniprogram_dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"pub": "npm run build && npm publish --access public",
|
||||
"build:js": "tsc --project tsconfig_tsc.json"
|
||||
},
|
||||
"miniprogram": "miniprogram_dist",
|
||||
"author": "",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"@types/wechat-miniprogram": "^3.4.8",
|
||||
"dts-bundle": "^0.7.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.96.1",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
## 安装/更新
|
||||
|
||||
1、终端使用命令安装 npm 包
|
||||
|
||||
```bash
|
||||
npm install --save @jdmini/components@latest
|
||||
```
|
||||
|
||||
2、在小程序开发者工具中:菜单选择工具 -> 构建 npm
|
||||
|
||||
`注意:依赖@jdmini/api,请确保小程序项目已安装@jdmini/api`
|
||||
|
||||
## 使用
|
||||
|
||||
1、在页面的 json 文件中引入组件:
|
||||
|
||||
```json
|
||||
{
|
||||
"usingComponents": {
|
||||
"jdwx-ad": "@jdmini/components/jdwx-ad",
|
||||
"jdwx-link": "@jdmini/components/jdwx-link"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2、在页面的 wxml 文件中使用组件:
|
||||
|
||||
```html
|
||||
<jdwx-ad type="custom" />
|
||||
<jdwx-link />
|
||||
```
|
||||
BIN
node_modules/@jdmini/components/miniprogram_dist/icons/home-active.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
BIN
node_modules/@jdmini/components/miniprogram_dist/icons/link-active.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
import { adManager } from '@jdmini/api'
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
type: {
|
||||
type: String,
|
||||
value: 'custom' // 可选banner, video, custom
|
||||
}
|
||||
},
|
||||
data: {
|
||||
ads: {}
|
||||
},
|
||||
lifetimes: {
|
||||
attached: function () {
|
||||
adManager.onDataReady(() => {
|
||||
this.setData({ ads: adManager.ads })
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
})
|
||||
3
node_modules/@jdmini/components/miniprogram_dist/jdwx-ad/index.json
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
||||
5
node_modules/@jdmini/components/miniprogram_dist/jdwx-ad/index.wxml
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<view class="jdwx-ad-component">
|
||||
<ad wx:if="{{type === 'banner' && ads.banner}}" class="jdwx-ad-item" unit-id="{{ads.banner}}"></ad>
|
||||
<ad wx:if="{{type === 'video' && ads.video}}" class="jdwx-ad-item" ad-type="video" unit-id="{{ads.video}}"></ad>
|
||||
<ad-custom wx:if="{{type === 'custom' && ads.custom}}" class="jdwx-ad-item" unit-id="{{ads.custom}}"></ad-custom>
|
||||
</view>
|
||||
7
node_modules/@jdmini/components/miniprogram_dist/jdwx-ad/index.wxss
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.jdwx-ad-component {
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
.jdwx-ad-item {
|
||||
bottom: 10rpx;
|
||||
}
|
||||
37
node_modules/@jdmini/components/miniprogram_dist/jdwx-link/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { adManager } from '@jdmini/api'
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
},
|
||||
data: {
|
||||
link: [],
|
||||
top: {},
|
||||
},
|
||||
pageLifetimes: {
|
||||
show: function () {
|
||||
adManager.onDataReady(() => {
|
||||
this.setData({
|
||||
link: adManager.link,
|
||||
top: adManager.top
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
gotopLink: function () {
|
||||
wx.vibrateShort()
|
||||
wx.openEmbeddedMiniProgram({
|
||||
appId: this.data.top.appId,
|
||||
path: this.data.top.linkPage
|
||||
});
|
||||
},
|
||||
goLink: function (e) {
|
||||
let index = e.currentTarget.id
|
||||
wx.vibrateShort()
|
||||
wx.openEmbeddedMiniProgram({
|
||||
appId: this.data.link[index].appId,
|
||||
path: this.data.link[index].linkPage
|
||||
});
|
||||
},
|
||||
}
|
||||
})
|
||||
3
node_modules/@jdmini/components/miniprogram_dist/jdwx-link/index.json
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
||||
11
node_modules/@jdmini/components/miniprogram_dist/jdwx-link/index.wxml
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<view class="jdwx-link-component">
|
||||
<view wx:if="{{top.appId}}" class="jdwx-applink-top" bindtap="gotopLink">
|
||||
<view><image src="{{top.appLogo}}" class="jdwx-applink-icon" /> </view>
|
||||
<view ><text class="jdwx-applink-top-linkname">{{top.linkName}}</text>
|
||||
<text class="jdwx-applink-top-text">{{top.appDsc}}</text> </view>
|
||||
</view>
|
||||
<view id="{{bindex}}" bindtap="goLink" wx:for="{{link}}" wx:for-index="bindex" wx:key="index" class="jdwx-applink-list">
|
||||
<image src="{{item.appLogo}}" class="jdwx-applink-icon" />
|
||||
<text class="jdwx-applink-text">{{item.linkName}}</text>
|
||||
</view>
|
||||
</view>
|
||||
63
node_modules/@jdmini/components/miniprogram_dist/jdwx-link/index.wxss
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* 页面容器 */
|
||||
.jdwx-link-component {
|
||||
/* background-image: linear-gradient(to right, #4F9863, #4F9863); */
|
||||
background-attachment: fixed;
|
||||
background-size: cover; /* 确保背景图像覆盖整个元素 */
|
||||
/* height: 100vh; */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
overflow: auto; /* 允许内容滚动 */
|
||||
}
|
||||
|
||||
/* 列表项样式 */
|
||||
.jdwx-applink-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 95%;
|
||||
/* 假设我们想要每个view的高度约为屏幕高度的1/8 */
|
||||
/* 使用小程序的wx.getSystemInfo API动态计算并设置这个值会更准确 */
|
||||
height: calc((100vh - 40rpx) / 10); /* 减去容器padding的影响 */
|
||||
padding: 20rpx;
|
||||
background-color: rgba(248, 250, 252, 1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.jdwx-applink-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 95%;
|
||||
/* 假设我们想要每个view的高度约为屏幕高度的1/8 */
|
||||
/* 使用小程序的wx.getSystemInfo API动态计算并设置这个值会更准确 */
|
||||
height: calc((100vh - 40rpx) / 6); /* 减去容器padding的影响 */
|
||||
padding: 20rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 30rpx;
|
||||
background-color: rgba(248, 250, 252, 1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.jdwx-applink-top-linkname{
|
||||
display: flex;
|
||||
font-size: 36rpx;
|
||||
color: rgb(39, 37, 37);
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
/* 图标样式 */
|
||||
.jdwx-applink-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 50rpx;
|
||||
margin-left: 30rpx;
|
||||
}
|
||||
|
||||
/* 文本样式 */
|
||||
.jdwx-applink-text {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: rgb(39, 37, 37);
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "@jdmini/components",
|
||||
"version": "1.0.6",
|
||||
"description": "",
|
||||
"files": [
|
||||
"miniprogram_dist",
|
||||
"resources"
|
||||
],
|
||||
"scripts": {
|
||||
"pub": "npm publish --access public"
|
||||
},
|
||||
"miniprogram": "miniprogram_dist",
|
||||
"author": "",
|
||||
"peerDependencies": {
|
||||
"@jdmini/api": ">=1.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/wechat-miniprogram": "^3.4.8"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 150
|
||||
|
||||
[CHANGELOG.md]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.json]
|
||||
max_line_length = off
|
||||
|
||||
[Makefile]
|
||||
max_line_length = off
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"root": true,
|
||||
|
||||
"extends": "@ljharb",
|
||||
|
||||
"rules": {
|
||||
"complexity": [2, 20],
|
||||
"eqeqeq": [2, "allow-null"],
|
||||
"func-name-matching": [1],
|
||||
"max-depth": [1, 4],
|
||||
"max-statements": [2, 26],
|
||||
"no-extra-parens": [1],
|
||||
"no-magic-numbers": [0],
|
||||
"no-restricted-syntax": [2, "BreakStatement", "ContinueStatement", "DebuggerStatement", "LabeledStatement", "WithStatement"],
|
||||
"sort-keys": [0],
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
{
|
||||
"es3": true,
|
||||
|
||||
"additionalRules": [],
|
||||
|
||||
"requireSemicolons": true,
|
||||
|
||||
"disallowMultipleSpaces": true,
|
||||
|
||||
"disallowIdentifierNames": [],
|
||||
|
||||
"requireCurlyBraces": {
|
||||
"allExcept": [],
|
||||
"keywords": ["if", "else", "for", "while", "do", "try", "catch"]
|
||||
},
|
||||
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "function"],
|
||||
|
||||
"disallowSpaceAfterKeywords": [],
|
||||
|
||||
"disallowSpaceBeforeComma": true,
|
||||
"disallowSpaceAfterComma": false,
|
||||
"disallowSpaceBeforeSemicolon": true,
|
||||
|
||||
"disallowNodeTypes": [
|
||||
"DebuggerStatement",
|
||||
"LabeledStatement",
|
||||
"SwitchCase",
|
||||
"SwitchStatement",
|
||||
"WithStatement"
|
||||
],
|
||||
|
||||
"requireObjectKeysOnNewLine": { "allExcept": ["sameLine"] },
|
||||
|
||||
"requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true },
|
||||
"requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true },
|
||||
"disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true },
|
||||
"requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true },
|
||||
"disallowSpacesInFunctionDeclaration": { "beforeOpeningRoundBrace": true },
|
||||
|
||||
"requireSpaceBetweenArguments": true,
|
||||
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
|
||||
"disallowSpacesInsideArrayBrackets": true,
|
||||
|
||||
"disallowQuotedKeysInObjects": { "allExcept": ["reserved"] },
|
||||
|
||||
"disallowSpaceAfterObjectKeys": true,
|
||||
|
||||
"requireCommaBeforeLineBreak": true,
|
||||
|
||||
"disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
|
||||
"requireSpaceAfterPrefixUnaryOperators": [],
|
||||
|
||||
"disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
|
||||
"requireSpaceBeforePostfixUnaryOperators": [],
|
||||
|
||||
"disallowSpaceBeforeBinaryOperators": [],
|
||||
"requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
|
||||
|
||||
"requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
|
||||
"disallowSpaceAfterBinaryOperators": [],
|
||||
|
||||
"disallowImplicitTypeConversion": ["binary", "string"],
|
||||
|
||||
"disallowKeywords": ["with", "eval"],
|
||||
|
||||
"requireKeywordsOnNewLine": [],
|
||||
"disallowKeywordsOnNewLine": ["else"],
|
||||
|
||||
"requireLineFeedAtFileEnd": true,
|
||||
|
||||
"disallowTrailingWhitespace": true,
|
||||
|
||||
"disallowTrailingComma": true,
|
||||
|
||||
"excludeFiles": ["node_modules/**", "vendor/**"],
|
||||
|
||||
"disallowMultipleLineStrings": true,
|
||||
|
||||
"requireDotNotation": { "allExcept": ["keywords"] },
|
||||
|
||||
"requireParenthesesAroundIIFE": true,
|
||||
|
||||
"validateLineBreaks": "LF",
|
||||
|
||||
"validateQuoteMarks": {
|
||||
"escape": true,
|
||||
"mark": "'"
|
||||
},
|
||||
|
||||
"disallowOperatorBeforeLineBreak": [],
|
||||
|
||||
"requireSpaceBeforeKeywords": [
|
||||
"do",
|
||||
"for",
|
||||
"if",
|
||||
"else",
|
||||
"switch",
|
||||
"case",
|
||||
"try",
|
||||
"catch",
|
||||
"finally",
|
||||
"while",
|
||||
"with",
|
||||
"return"
|
||||
],
|
||||
|
||||
"validateAlignedFunctionParameters": {
|
||||
"lineBreakAfterOpeningBraces": true,
|
||||
"lineBreakBeforeClosingBraces": true
|
||||
},
|
||||
|
||||
"requirePaddingNewLinesBeforeExport": true,
|
||||
|
||||
"validateNewlineAfterArrayElements": {
|
||||
"maximum": 6
|
||||
},
|
||||
|
||||
"requirePaddingNewLinesAfterUseStrict": true,
|
||||
|
||||
"disallowArrowFunctions": true,
|
||||
|
||||
"disallowMultiLineTernary": true,
|
||||
|
||||
"validateOrderInObjectKeys": false,
|
||||
|
||||
"disallowIdenticalDestructuringNames": true,
|
||||
|
||||
"disallowNestedTernaries": { "maxLevel": 1 },
|
||||
|
||||
"requireSpaceAfterComma": { "allExcept": ["trailing"] },
|
||||
"requireAlignedMultilineParams": false,
|
||||
|
||||
"requireSpacesInGenerator": {
|
||||
"afterStar": true
|
||||
},
|
||||
|
||||
"disallowSpacesInGenerator": {
|
||||
"beforeStar": true
|
||||
},
|
||||
|
||||
"disallowVar": false,
|
||||
|
||||
"requireArrayDestructuring": false,
|
||||
|
||||
"requireEnhancedObjectLiterals": false,
|
||||
|
||||
"requireObjectDestructuring": false,
|
||||
|
||||
"requireEarlyReturn": false,
|
||||
|
||||
"requireCapitalizedConstructorsNew": {
|
||||
"allExcept": ["Function", "String", "Object", "Symbol", "Number", "Date", "RegExp", "Error", "Boolean", "Array"]
|
||||
},
|
||||
|
||||
"requireImportAlphabetized": false,
|
||||
|
||||
"requireSpaceBeforeObjectValues": true,
|
||||
"requireSpaceBeforeDestructuredValues": true,
|
||||
|
||||
"disallowSpacesInsideTemplateStringPlaceholders": true,
|
||||
|
||||
"disallowArrayDestructuringReturn": false,
|
||||
|
||||
"requireNewlineBeforeSingleStatementsInIf": false,
|
||||
|
||||
"disallowUnusedVariables": true,
|
||||
|
||||
"requireSpacesInsideImportedObjectBraces": true,
|
||||
|
||||
"requireUseStrict": true
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
language: node_js
|
||||
os:
|
||||
- linux
|
||||
node_js:
|
||||
- "10.7"
|
||||
- "9.11"
|
||||
- "8.11"
|
||||
- "7.10"
|
||||
- "6.14"
|
||||
- "5.12"
|
||||
- "4.9"
|
||||
- "iojs-v3.3"
|
||||
- "iojs-v2.5"
|
||||
- "iojs-v1.8"
|
||||
- "0.12"
|
||||
- "0.10"
|
||||
- "0.8"
|
||||
before_install:
|
||||
- 'case "${TRAVIS_NODE_VERSION}" in 0.*) export NPM_CONFIG_STRICT_SSL=false ;; esac'
|
||||
- 'nvm install-latest-npm'
|
||||
install:
|
||||
- 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ] || [ "${TRAVIS_NODE_VERSION}" = "0.9" ]; then nvm install --latest-npm 0.8 && npm install && nvm use "${TRAVIS_NODE_VERSION}"; else npm install; fi;'
|
||||
script:
|
||||
- 'if [ -n "${PRETEST-}" ]; then npm run pretest ; fi'
|
||||
- 'if [ -n "${POSTTEST-}" ]; then npm run posttest ; fi'
|
||||
- 'if [ -n "${COVERAGE-}" ]; then npm run coverage ; fi'
|
||||
- 'if [ -n "${TEST-}" ]; then npm run tests-only ; fi'
|
||||
sudo: false
|
||||
env:
|
||||
- TEST=true
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- node_js: "lts/*"
|
||||
env: PRETEST=true
|
||||
- node_js: "lts/*"
|
||||
env: POSTTEST=true
|
||||
- node_js: "4"
|
||||
env: COVERAGE=true
|
||||
- node_js: "10.6"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "10.5"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "10.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "10.3"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "10.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "10.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "10.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.10"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.9"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.8"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.7"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.6"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.5"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.3"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "9.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.10"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.9"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.8"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.7"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.6"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.5"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.3"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "8.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.9"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.8"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.7"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.6"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.5"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.3"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "7.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.13"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.12"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.11"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.10"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.9"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.8"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.7"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.6"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.5"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.3"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "6.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.11"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.10"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.9"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.8"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.7"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.6"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.5"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.3"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "5.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "4.8"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "4.7"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "4.6"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "4.5"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "4.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "4.3"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "4.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "4.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "4.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v3.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v3.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v3.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v2.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v2.3"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v2.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v2.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v2.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v1.7"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v1.6"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v1.5"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v1.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v1.3"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v1.2"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v1.1"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "iojs-v1.0"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "0.11"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "0.9"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "0.6"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
- node_js: "0.4"
|
||||
env: TEST=true ALLOW_FAILURE=true
|
||||
allow_failures:
|
||||
- os: osx
|
||||
- env: TEST=true ALLOW_FAILURE=true
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
3.0.2 / 2018-07-19
|
||||
==================
|
||||
* [Fix] Prevent merging `__proto__` property (#48)
|
||||
* [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`
|
||||
* [Tests] up to `node` `v10.7`, `v9.11`, `v8.11`, `v7.10`, `v6.14`, `v4.9`; use `nvm install-latest-npm`
|
||||
|
||||
3.0.1 / 2017-04-27
|
||||
==================
|
||||
* [Fix] deep extending should work with a non-object (#46)
|
||||
* [Dev Deps] update `tape`, `eslint`, `@ljharb/eslint-config`
|
||||
* [Tests] up to `node` `v7.9`, `v6.10`, `v4.8`; improve matrix
|
||||
* [Docs] Switch from vb.teelaun.ch to versionbadg.es for the npm version badge SVG.
|
||||
* [Docs] Add example to readme (#34)
|
||||
|
||||
3.0.0 / 2015-07-01
|
||||
==================
|
||||
* [Possible breaking change] Use global "strict" directive (#32)
|
||||
* [Tests] `int` is an ES3 reserved word
|
||||
* [Tests] Test up to `io.js` `v2.3`
|
||||
* [Tests] Add `npm run eslint`
|
||||
* [Dev Deps] Update `covert`, `jscs`
|
||||
|
||||
2.0.1 / 2015-04-25
|
||||
==================
|
||||
* Use an inline `isArray` check, for ES3 browsers. (#27)
|
||||
* Some old browsers fail when an identifier is `toString`
|
||||
* Test latest `node` and `io.js` versions on `travis-ci`; speed up builds
|
||||
* Add license info to package.json (#25)
|
||||
* Update `tape`, `jscs`
|
||||
* Adding a CHANGELOG
|
||||
|
||||
2.0.0 / 2014-10-01
|
||||
==================
|
||||
* Increase code coverage to 100%; run code coverage as part of tests
|
||||
* Add `npm run lint`; Run linter as part of tests
|
||||
* Remove nodeType and setInterval checks in isPlainObject
|
||||
* Updating `tape`, `jscs`, `covert`
|
||||
* General style and README cleanup
|
||||
|
||||
1.3.0 / 2014-06-20
|
||||
==================
|
||||
* Add component.json for browser support (#18)
|
||||
* Use SVG for badges in README (#16)
|
||||
* Updating `tape`, `covert`
|
||||
* Updating travis-ci to work with multiple node versions
|
||||
* Fix `deep === false` bug (returning target as {}) (#14)
|
||||
* Fixing constructor checks in isPlainObject
|
||||
* Adding additional test coverage
|
||||
* Adding `npm run coverage`
|
||||
* Add LICENSE (#13)
|
||||
* Adding a warning about `false`, per #11
|
||||
* General style and whitespace cleanup
|
||||
|
||||
1.2.1 / 2013-09-14
|
||||
==================
|
||||
* Fixing hasOwnProperty bugs that would only have shown up in specific browsers. Fixes #8
|
||||
* Updating `tape`
|
||||
|
||||
1.2.0 / 2013-09-02
|
||||
==================
|
||||
* Updating the README: add badges
|
||||
* Adding a missing variable reference.
|
||||
* Using `tape` instead of `buster` for tests; add more tests (#7)
|
||||
* Adding node 0.10 to Travis CI (#6)
|
||||
* Enabling "npm test" and cleaning up package.json (#5)
|
||||
* Add Travis CI.
|
||||
|
||||
1.1.3 / 2012-12-06
|
||||
==================
|
||||
* Added unit tests.
|
||||
* Ensure extend function is named. (Looks nicer in a stack trace.)
|
||||
* README cleanup.
|
||||
|
||||
1.1.1 / 2012-11-07
|
||||
==================
|
||||
* README cleanup.
|
||||
* Added installation instructions.
|
||||
* Added a missing semicolon
|
||||
|
||||
1.0.0 / 2012-04-08
|
||||
==================
|
||||
* Initial commit
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Stefan Thomas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
[![Build Status][travis-svg]][travis-url]
|
||||
[![dependency status][deps-svg]][deps-url]
|
||||
[![dev dependency status][dev-deps-svg]][dev-deps-url]
|
||||
|
||||
# extend() for Node.js <sup>[![Version Badge][npm-version-png]][npm-url]</sup>
|
||||
|
||||
`node-extend` is a port of the classic extend() method from jQuery. It behaves as you expect. It is simple, tried and true.
|
||||
|
||||
Notes:
|
||||
|
||||
* Since Node.js >= 4,
|
||||
[`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
|
||||
now offers the same functionality natively (but without the "deep copy" option).
|
||||
See [ECMAScript 2015 (ES6) in Node.js](https://nodejs.org/en/docs/es6).
|
||||
* Some native implementations of `Object.assign` in both Node.js and many
|
||||
browsers (since NPM modules are for the browser too) may not be fully
|
||||
spec-compliant.
|
||||
Check [`object.assign`](https://www.npmjs.com/package/object.assign) module for
|
||||
a compliant candidate.
|
||||
|
||||
## Installation
|
||||
|
||||
This package is available on [npm][npm-url] as: `extend`
|
||||
|
||||
``` sh
|
||||
npm install extend
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
**Syntax:** extend **(** [`deep`], `target`, `object1`, [`objectN`] **)**
|
||||
|
||||
*Extend one object with one or more others, returning the modified object.*
|
||||
|
||||
**Example:**
|
||||
|
||||
``` js
|
||||
var extend = require('extend');
|
||||
extend(targetObject, object1, object2);
|
||||
```
|
||||
|
||||
Keep in mind that the target object will be modified, and will be returned from extend().
|
||||
|
||||
If a boolean true is specified as the first argument, extend performs a deep copy, recursively copying any objects it finds. Otherwise, the copy will share structure with the original object(s).
|
||||
Undefined properties are not copied. However, properties inherited from the object's prototype will be copied over.
|
||||
Warning: passing `false` as the first argument is not supported.
|
||||
|
||||
### Arguments
|
||||
|
||||
* `deep` *Boolean* (optional)
|
||||
If set, the merge becomes recursive (i.e. deep copy).
|
||||
* `target` *Object*
|
||||
The object to extend.
|
||||
* `object1` *Object*
|
||||
The object that will be merged into the first.
|
||||
* `objectN` *Object* (Optional)
|
||||
More objects to merge into the first.
|
||||
|
||||
## License
|
||||
|
||||
`node-extend` is licensed under the [MIT License][mit-license-url].
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
All credit to the jQuery authors for perfecting this amazing utility.
|
||||
|
||||
Ported to Node.js by [Stefan Thomas][github-justmoon] with contributions by [Jonathan Buchanan][github-insin] and [Jordan Harband][github-ljharb].
|
||||
|
||||
[travis-svg]: https://travis-ci.org/justmoon/node-extend.svg
|
||||
[travis-url]: https://travis-ci.org/justmoon/node-extend
|
||||
[npm-url]: https://npmjs.org/package/extend
|
||||
[mit-license-url]: http://opensource.org/licenses/MIT
|
||||
[github-justmoon]: https://github.com/justmoon
|
||||
[github-insin]: https://github.com/insin
|
||||
[github-ljharb]: https://github.com/ljharb
|
||||
[npm-version-png]: http://versionbadg.es/justmoon/node-extend.svg
|
||||
[deps-svg]: https://david-dm.org/justmoon/node-extend.svg
|
||||
[deps-url]: https://david-dm.org/justmoon/node-extend
|
||||
[dev-deps-svg]: https://david-dm.org/justmoon/node-extend/dev-status.svg
|
||||
[dev-deps-url]: https://david-dm.org/justmoon/node-extend#info=devDependencies
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "extend",
|
||||
"author": "Stefan Thomas <justmoon@members.fsf.org> (http://www.justmoon.net)",
|
||||
"version": "3.0.0",
|
||||
"description": "Port of jQuery.extend for node.js and the browser.",
|
||||
"scripts": [
|
||||
"index.js"
|
||||
],
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Jordan Harband",
|
||||
"url": "https://github.com/ljharb"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"extend",
|
||||
"clone",
|
||||
"merge"
|
||||
],
|
||||
"repository" : {
|
||||
"type": "git",
|
||||
"url": "https://github.com/justmoon/node-extend.git"
|
||||
},
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"tape" : "~3.0.0",
|
||||
"covert": "~0.4.0",
|
||||
"jscs": "~1.6.2"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
'use strict';
|
||||
|
||||
var hasOwn = Object.prototype.hasOwnProperty;
|
||||
var toStr = Object.prototype.toString;
|
||||
var defineProperty = Object.defineProperty;
|
||||
var gOPD = Object.getOwnPropertyDescriptor;
|
||||
|
||||
var isArray = function isArray(arr) {
|
||||
if (typeof Array.isArray === 'function') {
|
||||
return Array.isArray(arr);
|
||||
}
|
||||
|
||||
return toStr.call(arr) === '[object Array]';
|
||||
};
|
||||
|
||||
var isPlainObject = function isPlainObject(obj) {
|
||||
if (!obj || toStr.call(obj) !== '[object Object]') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var hasOwnConstructor = hasOwn.call(obj, 'constructor');
|
||||
var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
|
||||
// Not own constructor property must be Object
|
||||
if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Own properties are enumerated firstly, so to speed up,
|
||||
// if last one is own, then all properties are own.
|
||||
var key;
|
||||
for (key in obj) { /**/ }
|
||||
|
||||
return typeof key === 'undefined' || hasOwn.call(obj, key);
|
||||
};
|
||||
|
||||
// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target
|
||||
var setProperty = function setProperty(target, options) {
|
||||
if (defineProperty && options.name === '__proto__') {
|
||||
defineProperty(target, options.name, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
value: options.newValue,
|
||||
writable: true
|
||||
});
|
||||
} else {
|
||||
target[options.name] = options.newValue;
|
||||
}
|
||||
};
|
||||
|
||||
// Return undefined instead of __proto__ if '__proto__' is not an own property
|
||||
var getProperty = function getProperty(obj, name) {
|
||||
if (name === '__proto__') {
|
||||
if (!hasOwn.call(obj, name)) {
|
||||
return void 0;
|
||||
} else if (gOPD) {
|
||||
// In early versions of node, obj['__proto__'] is buggy when obj has
|
||||
// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.
|
||||
return gOPD(obj, name).value;
|
||||
}
|
||||
}
|
||||
|
||||
return obj[name];
|
||||
};
|
||||
|
||||
module.exports = function extend() {
|
||||
var options, name, src, copy, copyIsArray, clone;
|
||||
var target = arguments[0];
|
||||
var i = 1;
|
||||
var length = arguments.length;
|
||||
var deep = false;
|
||||
|
||||
// Handle a deep copy situation
|
||||
if (typeof target === 'boolean') {
|
||||
deep = target;
|
||||
target = arguments[1] || {};
|
||||
// skip the boolean and the target
|
||||
i = 2;
|
||||
}
|
||||
if (target == null || (typeof target !== 'object' && typeof target !== 'function')) {
|
||||
target = {};
|
||||
}
|
||||
|
||||
for (; i < length; ++i) {
|
||||
options = arguments[i];
|
||||
// Only deal with non-null/undefined values
|
||||
if (options != null) {
|
||||
// Extend the base object
|
||||
for (name in options) {
|
||||
src = getProperty(target, name);
|
||||
copy = getProperty(options, name);
|
||||
|
||||
// Prevent never-ending loop
|
||||
if (target !== copy) {
|
||||
// Recurse if we're merging plain objects or arrays
|
||||
if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
|
||||
if (copyIsArray) {
|
||||
copyIsArray = false;
|
||||
clone = src && isArray(src) ? src : [];
|
||||
} else {
|
||||
clone = src && isPlainObject(src) ? src : {};
|
||||
}
|
||||
|
||||
// Never move original objects, clone them
|
||||
setProperty(target, { name: name, newValue: extend(deep, clone, copy) });
|
||||
|
||||
// Don't bring in undefined values
|
||||
} else if (typeof copy !== 'undefined') {
|
||||
setProperty(target, { name: name, newValue: copy });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the modified object
|
||||
return target;
|
||||
};
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"name": "extend",
|
||||
"author": "Stefan Thomas <justmoon@members.fsf.org> (http://www.justmoon.net)",
|
||||
"version": "3.0.2",
|
||||
"description": "Port of jQuery.extend for node.js and the browser",
|
||||
"main": "index",
|
||||
"scripts": {
|
||||
"pretest": "npm run lint",
|
||||
"test": "npm run tests-only",
|
||||
"posttest": "npm run coverage-quiet",
|
||||
"tests-only": "node test",
|
||||
"coverage": "covert test/index.js",
|
||||
"coverage-quiet": "covert test/index.js --quiet",
|
||||
"lint": "npm run jscs && npm run eslint",
|
||||
"jscs": "jscs *.js */*.js",
|
||||
"eslint": "eslint *.js */*.js"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Jordan Harband",
|
||||
"url": "https://github.com/ljharb"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"extend",
|
||||
"clone",
|
||||
"merge"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/justmoon/node-extend.git"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@ljharb/eslint-config": "^12.2.1",
|
||||
"covert": "^1.1.0",
|
||||
"eslint": "^4.19.1",
|
||||
"jscs": "^3.0.7",
|
||||
"tape": "^4.9.1"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
build/*.js
|
||||
src/qrcode.js
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
|
||||
extends: 'standard',
|
||||
"globals": {
|
||||
__VERSION__: false,
|
||||
ENV: false,
|
||||
wx: false
|
||||
},
|
||||
// add your custom rules here
|
||||
'rules': {
|
||||
// allow paren-less arrow functions
|
||||
'arrow-parens': 0,
|
||||
// allow async-await
|
||||
'generator-star-spacing': 0,
|
||||
// allow debugger during development
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# Description
|
||||
|
||||
[问题简单描述]
|
||||
|
||||
# Environment
|
||||
|
||||
* Platform: [开发者工具/iOS/Andriod/Web]
|
||||
* Platform version: [对应工具或者iOS或者Andriod的版本号]
|
||||
* Wechat version: [微信版本号]
|
||||
* weapp-qrcode version: [在package.json里]
|
||||
* other version: [如果在某一设备下出现该问题,请填写设备号]
|
||||
|
||||
# Reproduce
|
||||
|
||||
**问题复现步骤:**
|
||||
|
||||
1. [第一步]
|
||||
2. [第二步]
|
||||
3. [其他步骤...]
|
||||
|
||||
**期望的表现:**
|
||||
|
||||
[在这里描述期望的表现]
|
||||
|
||||
**观察到的表现:**
|
||||
|
||||
[在这里描述观察到的表现]
|
||||
|
||||
**屏幕截图和动态 GIF 图**
|
||||
|
||||

|
||||
|
||||
# Relevant Code / Logs
|
||||
|
||||
```
|
||||
// TODO(you): code or logs here to reproduce the problem
|
||||
```
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
language: node_js
|
||||
sudo: required
|
||||
node_js:
|
||||
- 10.0.0
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
before_install:
|
||||
- export TZ='Asia/Shanghai'
|
||||
install:
|
||||
- npm install
|
||||
script:
|
||||
- npm run publish
|
||||
|
||||
after_script:
|
||||
- git init
|
||||
- git config user.name "${USER_NAME}"
|
||||
- git config user.email "${USER_EMAIL}"
|
||||
- git add .
|
||||
- git commit -m "publish"
|
||||
- git push -f https://${access_token}@github.com/yingye/weapp-qrcode HEAD:master
|
||||
|
||||
branch: master
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# Change Log
|
||||
|
||||
Change log for weapp-qrcode. [Details at Github](https://github.com/yingye/weapp-qrcode)
|
||||
|
||||
## [1.0.0] - 2018-12-25
|
||||
|
||||
- 支持传入绘图上下文(CanvasContext).
|
||||
|
||||
## [1.0.0-beta] - 2018-12-13
|
||||
|
||||
- 支持二维码在 canvas 上绘制的起始位置.
|
||||
- 支持在二维码上绘制图片及绘制位置.
|
||||
|
||||
## [0.9.0] - 2018-05-31
|
||||
|
||||
- 支持绘制带中文的二维码.
|
||||
|
||||
## [0.8.0] - 2018-05-15
|
||||
|
||||
- 绘制二维码后添加回调函数.
|
||||
|
||||
## [0.7.0] - 2018-05-11
|
||||
|
||||
- 支持在小程序组件中绘制二维码.
|
||||
|
||||
## [0.6.0] - 2018-04-16
|
||||
|
||||
- Add multi-output.
|
||||
|
||||
## [0.5.0] - 2018-03-11
|
||||
|
||||
- Add version in weapp.qrcode.js.
|
||||
|
||||
## [0.4.0] - 2018-03-10
|
||||
|
||||
- Fix options.
|
||||
|
||||
## [0.3.0] - 2018-02-03
|
||||
|
||||
- Initial release.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 yingye
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||