From 42c5354978e83ac2081feca06cf79af19fdf88fb Mon Sep 17 00:00:00 2001 From: duanshuwen Date: Sun, 3 Aug 2025 11:45:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=95=86=E5=93=81=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/ImageSwiper/styles/index.scss | 1 - pages/goods/components/GoodConfirm/README.md | 234 ++++++++ pages/goods/components/GoodConfirm/demo.vue | 191 +++++++ .../GoodConfirm/images/商品2级 门票.png | Bin 0 -> 28415 bytes pages/goods/components/GoodConfirm/index.vue | 151 +++++ .../components/GoodConfirm/styles/index.scss | 239 ++++++++ pages/goods/index.vue | 39 ++ pages/goods/styles/index.scss | 78 +++ uni_modules/uni-popup/changelog.md | 100 ++++ .../components/uni-popup-dialog/keypress.js | 45 ++ .../uni-popup-dialog/uni-popup-dialog.vue | 327 +++++++++++ .../uni-popup-message/uni-popup-message.vue | 143 +++++ .../uni-popup-share/uni-popup-share.vue | 188 +++++++ .../components/uni-popup/i18n/en.json | 7 + .../components/uni-popup/i18n/index.js | 8 + .../components/uni-popup/i18n/zh-Hans.json | 7 + .../components/uni-popup/i18n/zh-Hant.json | 7 + .../components/uni-popup/keypress.js | 45 ++ .../uni-popup/components/uni-popup/popup.js | 26 + .../components/uni-popup/uni-popup.uvue | 90 +++ .../components/uni-popup/uni-popup.vue | 518 ++++++++++++++++++ uni_modules/uni-popup/package.json | 107 ++++ uni_modules/uni-popup/readme.md | 17 + uni_modules/uni-transition/changelog.md | 31 ++ .../uni-transition/createAnimation.js | 131 +++++ .../uni-transition/uni-transition.vue | 292 ++++++++++ uni_modules/uni-transition/package.json | 112 ++++ uni_modules/uni-transition/readme.md | 11 + 28 files changed, 3144 insertions(+), 1 deletion(-) create mode 100644 pages/goods/components/GoodConfirm/README.md create mode 100644 pages/goods/components/GoodConfirm/demo.vue create mode 100644 pages/goods/components/GoodConfirm/images/商品2级 门票.png create mode 100644 pages/goods/components/GoodConfirm/index.vue create mode 100644 pages/goods/components/GoodConfirm/styles/index.scss create mode 100644 uni_modules/uni-popup/changelog.md create mode 100644 uni_modules/uni-popup/components/uni-popup-dialog/keypress.js create mode 100644 uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue create mode 100644 uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue create mode 100644 uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue create mode 100644 uni_modules/uni-popup/components/uni-popup/i18n/en.json create mode 100644 uni_modules/uni-popup/components/uni-popup/i18n/index.js create mode 100644 uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json create mode 100644 uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json create mode 100644 uni_modules/uni-popup/components/uni-popup/keypress.js create mode 100644 uni_modules/uni-popup/components/uni-popup/popup.js create mode 100644 uni_modules/uni-popup/components/uni-popup/uni-popup.uvue create mode 100644 uni_modules/uni-popup/components/uni-popup/uni-popup.vue create mode 100644 uni_modules/uni-popup/package.json create mode 100644 uni_modules/uni-popup/readme.md create mode 100644 uni_modules/uni-transition/changelog.md create mode 100644 uni_modules/uni-transition/components/uni-transition/createAnimation.js create mode 100644 uni_modules/uni-transition/components/uni-transition/uni-transition.vue create mode 100644 uni_modules/uni-transition/package.json create mode 100644 uni_modules/uni-transition/readme.md diff --git a/components/ImageSwiper/styles/index.scss b/components/ImageSwiper/styles/index.scss index 69d2d85..a7d1329 100644 --- a/components/ImageSwiper/styles/index.scss +++ b/components/ImageSwiper/styles/index.scss @@ -51,7 +51,6 @@ flex-shrink: 0; text-align: center; transition: all 0.3s ease; - cursor: pointer; &.active { image { diff --git a/pages/goods/components/GoodConfirm/README.md b/pages/goods/components/GoodConfirm/README.md new file mode 100644 index 0000000..340cce3 --- /dev/null +++ b/pages/goods/components/GoodConfirm/README.md @@ -0,0 +1,234 @@ +# GoodConfirm 商品确认组件 + +基于 uni-popup 弹出层的商品确认组件,提供优雅的商品购买确认界面。 + +## 功能特性 + +- 🎨 **现代化设计** - 采用底部弹出设计,符合移动端交互习惯 +- 📱 **响应式布局** - 完美适配各种屏幕尺寸 +- 🛒 **商品信息展示** - 支持商品图片、标题、价格、标签展示 +- 🔢 **数量选择** - 提供加减按钮和手动输入两种方式 +- 💰 **实时计算** - 自动计算并显示总价 +- ⚡ **性能优化** - 基于 Vue 3 Composition API,性能卓越 +- 🎭 **动画效果** - 流畅的弹出和交互动画 +- 🔧 **高度可配置** - 支持自定义商品数据和事件处理 + +## 基础用法 + +### 默认使用 + +```vue + + + +``` + +### 自定义商品数据 + +```vue + +``` + +## API 文档 + +### Props + +| 属性名 | 类型 | 默认值 | 说明 | +|--------|------|--------|------| +| goodsData | Object | {} | 商品数据对象 | + +#### goodsData 对象结构 + +```typescript +interface GoodsData { + commodityName?: string; // 商品名称 + price?: number; // 商品价格 + timeTag?: string; // 时间标签(如:随时可退) + commodityPhotoList?: Array<{ // 商品图片列表 + photoUrl: string; // 图片URL + }>; +} +``` + +### Events + +| 事件名 | 参数 | 说明 | +|--------|------|------| +| confirm | orderData | 确认购买时触发 | +| close | - | 关闭弹窗时触发 | + +#### confirm 事件参数 + +```typescript +interface OrderData { + goodsData: GoodsData; // 商品数据 + quantity: number; // 购买数量 + totalPrice: string; // 总价(字符串格式) +} +``` + +### Methods + +| 方法名 | 参数 | 说明 | +|--------|------|------| +| showPopup | - | 显示弹窗 | +| closePopup | - | 关闭弹窗 | + +## 样式定制 + +组件使用 SCSS 编写样式,支持以下自定义变量: + +```scss +// 主色调 +$primary-color: #ff6b35; +$primary-gradient: linear-gradient(135deg, #ff6b35 0%, #ff8f65 100%); + +// 文字颜色 +$text-primary: #333; +$text-secondary: #666; + +// 背景颜色 +$bg-white: #fff; +$bg-gray: #f8f9fa; +$border-color: #f5f5f5; + +// 圆角 +$border-radius: 8px; +$border-radius-large: 20px; +``` + +## 高级用法 + +### 响应式数据绑定 + +```vue + +``` + +### 订单处理集成 + +```vue + +``` + +## 注意事项 + +1. **依赖要求**:组件依赖 `uni-popup` 和 `uni-icons`,请确保项目中已安装相关依赖 +2. **图片资源**:请确保商品图片路径正确,建议使用绝对路径或网络图片 +3. **数量限制**:组件默认最小购买数量为 1,可根据业务需求调整 +4. **价格格式**:价格支持数字类型,组件内部会自动处理格式化 +5. **事件处理**:建议在 `confirm` 事件中添加适当的错误处理和用户反馈 + +## 更新日志 + +### v1.0.0 (2024-01-XX) +- ✨ 初始版本发布 +- 🎨 基于 uni-popup 的底部弹出设计 +- 🛒 完整的商品信息展示功能 +- 🔢 数量选择和总价计算 +- 📱 响应式移动端适配 +- 🎭 流畅的动画效果 +- 📚 完整的文档和示例 + +## 技术栈 + +- **框架**: Vue 3 + Composition API +- **UI组件**: uni-app + uni-ui +- **样式**: SCSS +- **构建工具**: Vite + +## 浏览器支持 + +- iOS Safari 10+ +- Android Chrome 50+ +- 微信小程序 +- 支付宝小程序 +- H5 现代浏览器 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/pages/goods/components/GoodConfirm/demo.vue b/pages/goods/components/GoodConfirm/demo.vue new file mode 100644 index 0000000..9be3fc8 --- /dev/null +++ b/pages/goods/components/GoodConfirm/demo.vue @@ -0,0 +1,191 @@ + + + + + \ No newline at end of file diff --git a/pages/goods/components/GoodConfirm/images/商品2级 门票.png b/pages/goods/components/GoodConfirm/images/商品2级 门票.png new file mode 100644 index 0000000000000000000000000000000000000000..25dc8bb145347ccb5544f24a2a468c7836d8f6d7 GIT binary patch literal 28415 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 literal 0 HcmV?d00001 diff --git a/pages/goods/components/GoodConfirm/index.vue b/pages/goods/components/GoodConfirm/index.vue new file mode 100644 index 0000000..e0062b1 --- /dev/null +++ b/pages/goods/components/GoodConfirm/index.vue @@ -0,0 +1,151 @@ + + + + + \ No newline at end of file diff --git a/pages/goods/components/GoodConfirm/styles/index.scss b/pages/goods/components/GoodConfirm/styles/index.scss new file mode 100644 index 0000000..6de10ed --- /dev/null +++ b/pages/goods/components/GoodConfirm/styles/index.scss @@ -0,0 +1,239 @@ +.good-confirm-container { + background: #fff; + border-radius: 20px 20px 0 0; + padding: 0; + max-height: 80vh; + overflow: hidden; + + .header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 20px 16px; + border-bottom: 1px solid #f5f5f5; + position: relative; + + .header-title { + font-size: 18px; + font-weight: 600; + color: #333; + flex: 1; + text-align: center; + } + + .close-btn { + position: absolute; + right: 20px; + top: 50%; + transform: translateY(-50%); + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 16px; + background: #f8f8f8; + transition: background 0.2s; + + &:active { + background: #e8e8e8; + } + } + } + + .goods-info { + display: flex; + padding: 20px; + gap: 12px; + border-bottom: 1px solid #f5f5f5; + + .goods-image { + width: 80px; + height: 80px; + border-radius: 8px; + overflow: hidden; + flex-shrink: 0; + + image { + width: 100%; + height: 100%; + object-fit: cover; + } + } + + .goods-details { + flex: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + + .goods-title { + font-size: 16px; + font-weight: 500; + color: #333; + line-height: 22px; + margin-bottom: 8px; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .goods-price { + display: flex; + align-items: baseline; + margin-bottom: 8px; + + .currency { + font-size: 14px; + color: #ff6b35; + font-weight: 500; + } + + .price { + font-size: 20px; + color: #ff6b35; + font-weight: 600; + margin-left: 2px; + } + } + + .goods-tag { + display: inline-block; + padding: 2px 8px; + background: #fff2e8; + color: #ff6b35; + font-size: 12px; + border-radius: 4px; + border: 1px solid #ffdbcc; + align-self: flex-start; + } + } + } + + .quantity-section { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px; + border-bottom: 1px solid #f5f5f5; + + .quantity-label { + font-size: 16px; + color: #333; + font-weight: 500; + } + + .quantity-control { + display: flex; + align-items: center; + gap: 0; + border: 1px solid #e8e8e8; + border-radius: 6px; + overflow: hidden; + + .quantity-btn { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + background: #f8f8f8; + transition: background 0.2s; + + &:active:not(.disabled) { + background: #e8e8e8; + } + + &.disabled { + opacity: 0.4; + pointer-events: none; + } + } + + .quantity-input { + width: 60px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + background: #fff; + border-left: 1px solid #e8e8e8; + border-right: 1px solid #e8e8e8; + + input { + width: 100%; + height: 100%; + text-align: center; + border: none; + outline: none; + font-size: 16px; + color: #333; + background: transparent; + } + } + } + } + + .total-section { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px; + background: #f8f9fa; + + .total-label { + font-size: 16px; + color: #333; + font-weight: 500; + } + + .total-price { + display: flex; + align-items: baseline; + + .currency { + font-size: 16px; + color: #ff6b35; + font-weight: 600; + } + + .price { + font-size: 24px; + color: #ff6b35; + font-weight: 700; + margin-left: 2px; + } + } + } + + .footer { + padding: 20px; + background: #fff; + + .confirm-btn { + width: 100%; + height: 48px; + background: linear-gradient(135deg, #ff6b35 0%, #ff8f65 100%); + color: #fff; + border: none; + border-radius: 24px; + font-size: 16px; + font-weight: 600; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + box-shadow: 0 4px 12px rgba(255, 107, 53, 0.3); + + &:active { + transform: translateY(1px); + box-shadow: 0 2px 8px rgba(255, 107, 53, 0.3); + } + + &::after { + border: none; + } + } + } +} \ No newline at end of file diff --git a/pages/goods/index.vue b/pages/goods/index.vue index 877be27..010f430 100644 --- a/pages/goods/index.vue +++ b/pages/goods/index.vue @@ -12,8 +12,21 @@ + + + + + + + + @@ -25,8 +38,11 @@ import TopNavBar from "@/components/TopNavBar/index.vue"; import ImageSwiper from "@/components/ImageSwiper/index.vue"; import GoodInfo from "./components/GoodInfo/index.vue"; import ModuleTitle from "@/components/ModuleTitle/index.vue"; +import GoodConfirm from "./components/GoodConfirm/index.vue"; const goodsData = ref({}); +const goodConfirmRef = ref(null); + // 获取商品详情数据 const goodsInfo = async (params) => { const res = await goodsDetail(params); @@ -34,6 +50,29 @@ const goodsInfo = async (params) => { goodsData.value = res.data; }; +// 显示确认弹窗 +const showConfirmPopup = () => { + goodConfirmRef.value?.showPopup(); +}; + +// 处理确认订单 +const handleConfirmOrder = (orderData) => { + console.log("确认订单:", orderData); + uni.showToast({ + title: "订单确认成功", + icon: "success", + }); + // 这里可以跳转到订单页面或支付页面 + // uni.navigateTo({ + // url: '/pages/order/detail?orderId=' + orderData.orderId + // }); +}; + +// 处理关闭弹窗 +const handleCloseConfirm = () => { + console.log("关闭确认弹窗"); +}; + onLoad(({ commodityId = "1950766939442774018" }) => { goodsInfo({ commodityId }); }); diff --git a/pages/goods/styles/index.scss b/pages/goods/styles/index.scss index eb6424c..0b2010e 100644 --- a/pages/goods/styles/index.scss +++ b/pages/goods/styles/index.scss @@ -1,3 +1,6 @@ +$button-color: #00a6ff; +$button-hover-color: darken($button-color, 8%); + .goods-container { min-height: 100vh; background-color: #fff; @@ -5,6 +8,8 @@ .content-wrapper { // 为固定导航栏预留空间 padding-top: calc(var(--status-bar-height, 44px) + 68px); + // 为安全区预留空间 + padding-bottom: calc(var(--safe-area-inset-bottom, 0px) + 100px); } .goods-content { @@ -15,4 +20,77 @@ margin-top: -30px; z-index: 1; } + + .footer { + position: fixed; + left: 0; + right: 0; + bottom: 0; + background-color: #fff; + padding-top: 12px; + padding-left: 12px; + padding-right: 12px; + box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08); + // 为安全区预留空间 + padding-bottom: var(--safe-area-inset-bottom, 0); + + .buy-button { + width: 100%; + background: linear-gradient(179deg, #00a6ff 0%, #0256ff 100%); + color: #fff; + border: none; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50px; + height: 42px; + font-size: 14px; + font-weight: 500; + margin-top: 12px; + position: relative; + overflow: hidden; + transition: all 0.3s ease; + letter-spacing: 0.5px; + + // 按钮波纹效果 + &::before { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + background: rgba(255, 255, 255, 0.3); + border-radius: 50%; + transform: translate(-50%, -50%); + transition: width 0.6s, height 0.6s; + } + + &:hover { + background: linear-gradient( + 135deg, + $button-hover-color 0%, + darken($button-hover-color, 5%) 100% + ); + transform: translateY(-2px); + box-shadow: 0 4px 16px rgba($button-color, 0.4); + + &::before { + width: 300px; + height: 300px; + } + } + + &:active { + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba($button-color, 0.3); + } + + &:focus { + outline: none; + box-shadow: 0 0 0 3px rgba($button-color, 0.3); + } + } + } } diff --git a/uni_modules/uni-popup/changelog.md b/uni_modules/uni-popup/changelog.md new file mode 100644 index 0000000..20b1e8c --- /dev/null +++ b/uni_modules/uni-popup/changelog.md @@ -0,0 +1,100 @@ +## 1.9.10(2025-07-18) +- 修复 nvue 下弹窗样式错乱的问题 ,更新依赖 uni-transition 组件 +- 更新 示例取消 borderRadius 属性 ,如需内容圆角,用户应该直接在内容插槽中实现 +## 1.9.9(2025-06-11) +- 修复 uni-popup-dialog 中 setVal 方法报错的问题 +- 修复 uni-popup-dialog 数据双向绑定问题。 +## 1.9.8(2025-04-16) +- 修复 更新组件示例 ,解决更新数据或保存项目导致弹窗消失的问题 +## 1.9.7(2025-04-14) +- 修复 uni-popup-dialog 弹出框在vue3中双向绑定问题 +## 1.9.6(2025-01-08) +- 修复 示例中过期图片地址 +## 1.9.5(2024-10-15) +- 修复 微信小程序中的getSystemInfo警告 +## 1.9.2(2024-09-21) +- 修复 uni-popup在android上的重复点击弹出位置不正确的bug +## 1.9.1(2024-04-02) +- 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法) +## 1.9.0(2024-03-28) +- 修复 uni-popup-dialog 双向绑定时初始化逻辑修正 +## 1.8.9(2024-03-20) +- 修复 uni-popup-dialog 数据输入时修正为双向绑定 +## 1.8.8(2024-02-20) +- 修复 uni-popup 在微信小程序下出现文字向上闪动的bug +## 1.8.7(2024-02-02) +- 新增 uni-popup-dialog 新增属性focus:input模式下,是否自动自动聚焦 +## 1.8.6(2024-01-30) +- 新增 uni-popup-dialog 新增属性maxLength:限制输入框字数 +## 1.8.5(2024-01-26) +- 新增 uni-popup-dialog 新增属性showClose:控制关闭按钮的显示 +## 1.8.4(2023-11-15) +- 新增 uni-popup 支持uni-app-x 注意暂时仅支持 `maskClick` `@open` `@close` +## 1.8.3(2023-04-17) +- 修复 uni-popup 重复打开时的 bug +## 1.8.2(2023-02-02) +- uni-popup-dialog 组件新增 inputType 属性 +## 1.8.1(2022-12-01) +- 修复 nvue 下 v-show 报错 +## 1.8.0(2022-11-29) +- 优化 主题样式 +## 1.7.9(2022-04-02) +- 修复 弹出层内部无法滚动的bug +## 1.7.8(2022-03-28) +- 修复 小程序中高度错误的bug +## 1.7.7(2022-03-17) +- 修复 快速调用open出现问题的Bug +## 1.7.6(2022-02-14) +- 修复 safeArea 属性不能设置为false的bug +## 1.7.5(2022-01-19) +- 修复 isMaskClick 失效的bug +## 1.7.4(2022-01-19) +- 新增 cancelText \ confirmText 属性 ,可自定义文本 +- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色 +- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题 +## 1.7.3(2022-01-13) +- 修复 设置 safeArea 属性不生效的bug +## 1.7.2(2021-11-26) +- 优化 组件示例 +## 1.7.1(2021-11-26) +- 修复 vuedoc 文字错误 +## 1.7.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup) +## 1.6.2(2021-08-24) +- 新增 支持国际化 +## 1.6.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.6.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.5.0(2021-06-23) +- 新增 mask-click 遮罩层点击事件 +## 1.4.5(2021-06-22) +- 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +## 1.4.4(2021-06-18) +- 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +## 1.4.3(2021-06-08) +- 修复 错误的 watch 字段 +- 修复 safeArea 属性不生效的问题 +- 修复 点击内容,再点击遮罩无法关闭的Bug +## 1.4.2(2021-05-12) +- 新增 组件示例地址 +## 1.4.1(2021-04-29) +- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题 +## 1.4.0 (2021-04-29) +- 新增 type 属性的 left\right 值,支持左右弹出 +- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗 +- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色 +- 新增 safeArea 属性,是否适配底部安全区 +- 修复 App\h5\微信小程序底部安全区占位不对的Bug +- 修复 App 端弹出等待的Bug +- 优化 提升低配设备性能,优化动画卡顿问题 +- 优化 更简单的组件自定义方式 +## 1.2.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.2.8(2021-02-05) +- 调整为uni_modules目录规范 +## 1.2.7(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 PC 端 +- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端 diff --git a/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js b/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js new file mode 100644 index 0000000..a747b9f --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + this.$once('hook:beforeDestroy', () => { + document.removeEventListener('keyup', listener) + }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue new file mode 100644 index 0000000..0295df0 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue @@ -0,0 +1,327 @@ + + + + + diff --git a/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue b/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue new file mode 100644 index 0000000..7f27a1e --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue @@ -0,0 +1,143 @@ + + + + diff --git a/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue new file mode 100644 index 0000000..049cd5c --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue @@ -0,0 +1,188 @@ + + + + diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/en.json b/uni_modules/uni-popup/components/uni-popup/i18n/en.json new file mode 100644 index 0000000..8c0f5f3 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/en.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "cancel", + "uni-popup.ok": "ok", + "uni-popup.placeholder": "pleace enter", + "uni-popup.title": "Hint", + "uni-popup.shareTitle": "Share to" +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/index.js b/uni_modules/uni-popup/components/uni-popup/i18n/index.js new file mode 100644 index 0000000..fa8f0f3 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json new file mode 100644 index 0000000..8e5b99f --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "取消", + "uni-popup.ok": "确定", + "uni-popup.placeholder": "请输入", + "uni-popup.title": "提示", + "uni-popup.shareTitle": "分享到" +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json new file mode 100644 index 0000000..06ce162 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "取消", + "uni-popup.ok": "確定", + "uni-popup.placeholder": "請輸入", + "uni-popup.title": "提示", + "uni-popup.shareTitle": "分享到" +} diff --git a/uni_modules/uni-popup/components/uni-popup/keypress.js b/uni_modules/uni-popup/components/uni-popup/keypress.js new file mode 100644 index 0000000..16a5818 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + // this.$once('hook:beforeDestroy', () => { + // document.removeEventListener('keyup', listener) + // }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-popup/components/uni-popup/popup.js b/uni_modules/uni-popup/components/uni-popup/popup.js new file mode 100644 index 0000000..a37fb9f --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/popup.js @@ -0,0 +1,26 @@ + +export default { + data() { + return { + + } + }, + created(){ + this.popup = this.getParent() + }, + methods:{ + /** + * 获取父元素实例 + */ + getParent(name = 'uniPopup') { + let parent = this.$parent; + let parentName = parent.$options.name; + while (parentName !== name) { + parent = parent.$parent; + if (!parent) return false + parentName = parent.$options.name; + } + return parent; + }, + } +} diff --git a/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue b/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue new file mode 100644 index 0000000..5eb8d5b --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue @@ -0,0 +1,90 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-popup/components/uni-popup/uni-popup.vue b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue new file mode 100644 index 0000000..5af55e0 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue @@ -0,0 +1,518 @@ + + + + diff --git a/uni_modules/uni-popup/package.json b/uni_modules/uni-popup/package.json new file mode 100644 index 0000000..ae01918 --- /dev/null +++ b/uni_modules/uni-popup/package.json @@ -0,0 +1,107 @@ +{ + "id": "uni-popup", + "displayName": "uni-popup 弹出层", + "version": "1.9.10", + "description": " Popup 组件,提供常用的弹层", + "keywords": [ + "uni-ui", + "弹出层", + "弹窗", + "popup", + "弹框" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "", + "uni-app": "^4.06", + "uni-app-x": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue", + "darkmode": "x", + "i18n": "x", + "widescreen": "x" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-transition" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app": { + "vue": { + "vue2": "√", + "vue3": "√" + }, + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "vue": "√", + "nvue": "√", + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√", + "alipay": "√", + "toutiao": "√", + "baidu": "√", + "kuaishou": "-", + "jd": "-", + "harmony": "-", + "qq": "√", + "lark": "-" + }, + "quickapp": { + "huawei": "-", + "union": "-" + } + }, + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-popup/readme.md b/uni_modules/uni-popup/readme.md new file mode 100644 index 0000000..fdad4b3 --- /dev/null +++ b/uni_modules/uni-popup/readme.md @@ -0,0 +1,17 @@ + + +## Popup 弹出层 +> **组件名:uni-popup** +> 代码块: `uPopup` +> 关联组件:`uni-transition` + + +弹出层组件,在应用中弹出一个消息提示窗口、提示框等 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + + + diff --git a/uni_modules/uni-transition/changelog.md b/uni_modules/uni-transition/changelog.md new file mode 100644 index 0000000..01bfb58 --- /dev/null +++ b/uni_modules/uni-transition/changelog.md @@ -0,0 +1,31 @@ +## 1.3.6(2025-07-18) +- 修复 nvue 页面,样式错误问题 +## 1.3.5(2025-06-11) +- 修复 第一次执行不显示动画的问题 +## 1.3.4(2025-04-16) +- 修复 页面数据更新到底动画复原的问题 +- 修复 示例页面打开报错的问题 +## 1.3.3(2024-04-23) +- 修复 当元素会受变量影响自动隐藏的bug +## 1.3.2(2023-05-04) +- 修复 NVUE 平台报错的问题 +## 1.3.1(2021-11-23) +- 修复 init 方法初始化问题 +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-transition](https://uniapp.dcloud.io/component/uniui/uni-transition) +## 1.2.1(2021-09-27) +- 修复 init 方法不生效的 Bug +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.1(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的 Bug +## 1.1.0(2021-04-22) +- 新增 通过方法自定义动画 +- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式 +- 优化 动画触发逻辑,使动画更流畅 +- 优化 支持单独的动画类型 +- 优化 文档示例 +## 1.0.2(2021-02-05) +- 调整为 uni_modules 目录规范 diff --git a/uni_modules/uni-transition/components/uni-transition/createAnimation.js b/uni_modules/uni-transition/components/uni-transition/createAnimation.js new file mode 100644 index 0000000..8f89b18 --- /dev/null +++ b/uni_modules/uni-transition/components/uni-transition/createAnimation.js @@ -0,0 +1,131 @@ +// const defaultOption = { +// duration: 300, +// timingFunction: 'linear', +// delay: 0, +// transformOrigin: '50% 50% 0' +// } +// #ifdef APP-NVUE +const nvueAnimation = uni.requireNativePlugin('animation') +// #endif +class MPAnimation { + constructor(options, _this) { + this.options = options + // 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误 + this.animation = uni.createAnimation({ + ...options + }) + this.currentStepAnimates = {} + this.next = 0 + this.$ = _this + + } + + _nvuePushAnimates(type, args) { + let aniObj = this.currentStepAnimates[this.next] + let styles = {} + if (!aniObj) { + styles = { + styles: {}, + config: {} + } + } else { + styles = aniObj + } + if (animateTypes1.includes(type)) { + if (!styles.styles.transform) { + styles.styles.transform = '' + } + let unit = '' + if(type === 'rotate'){ + unit = 'deg' + } + styles.styles.transform += `${type}(${args+unit}) ` + } else { + styles.styles[type] = `${args}` + } + this.currentStepAnimates[this.next] = styles + } + _animateRun(styles = {}, config = {}) { + let ref = this.$.$refs['ani'].ref + if (!ref) return + return new Promise((resolve, reject) => { + nvueAnimation.transition(ref, { + styles, + ...config + }, res => { + resolve() + }) + }) + } + + _nvueNextAnimate(animates, step = 0, fn) { + let obj = animates[step] + if (obj) { + let { + styles, + config + } = obj + this._animateRun(styles, config).then(() => { + step += 1 + this._nvueNextAnimate(animates, step, fn) + }) + } else { + this.currentStepAnimates = {} + typeof fn === 'function' && fn() + this.isEnd = true + } + } + + step(config = {}) { + // #ifndef APP-NVUE + this.animation.step(config) + // #endif + // #ifdef APP-NVUE + this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config) + this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin + this.next++ + // #endif + return this + } + + run(fn) { + // #ifndef APP-NVUE + this.$.animationData = this.animation.export() + this.$.timer = setTimeout(() => { + typeof fn === 'function' && fn() + }, this.$.durationTime) + // #endif + // #ifdef APP-NVUE + this.isEnd = false + let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref + if(!ref) return + this._nvueNextAnimate(this.currentStepAnimates, 0, fn) + this.next = 0 + // #endif + } +} + + +const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', + 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', + 'translateZ' +] +const animateTypes2 = ['opacity', 'backgroundColor'] +const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom'] +animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => { + MPAnimation.prototype[type] = function(...args) { + // #ifndef APP-NVUE + this.animation[type](...args) + // #endif + // #ifdef APP-NVUE + this._nvuePushAnimates(type, args) + // #endif + return this + } +}) + +export function createAnimation(option, _this) { + if(!_this) return + clearTimeout(_this.timer) + return new MPAnimation(option, _this) +} diff --git a/uni_modules/uni-transition/components/uni-transition/uni-transition.vue b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue new file mode 100644 index 0000000..baea9df --- /dev/null +++ b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue @@ -0,0 +1,292 @@ + + + + + diff --git a/uni_modules/uni-transition/package.json b/uni_modules/uni-transition/package.json new file mode 100644 index 0000000..0542c52 --- /dev/null +++ b/uni_modules/uni-transition/package.json @@ -0,0 +1,112 @@ +{ + "id": "uni-transition", + "displayName": "uni-transition 过渡动画", + "version": "1.3.6", + "description": "元素的简单过渡动画", + "keywords": [ + "uni-ui", + "uniui", + "动画", + "过渡", + "过渡动画" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "", + "uni-app": "^4.12", + "uni-app-x": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue", + "darkmode": "x", + "i18n": "x", + "widescreen": "x" + }, + "uni_modules": { + "dependencies": [ + "uni-scss" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app": { + "vue": { + "vue2": "√", + "vue3": "√" + }, + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "vue": "√", + "nvue": "√", + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": { + }, + "alipay": { + }, + "toutiao": { + }, + "baidu": { + }, + "kuaishou": { + }, + "jd": { + }, + "harmony": "-", + "qq": "√", + "lark": "-" + }, + "quickapp": { + "huawei": "√", + "union": "√" + } + }, + "uni-app-x": { + "web": { + "safari": "-", + "chrome": "-" + }, + "app": { + "android": "-", + "ios": "-", + "harmony": "-" + }, + "mp": { + "weixin": "-" + } + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-transition/readme.md b/uni_modules/uni-transition/readme.md new file mode 100644 index 0000000..2f8a77e --- /dev/null +++ b/uni_modules/uni-transition/readme.md @@ -0,0 +1,11 @@ + + +## Transition 过渡动画 +> **组件名:uni-transition** +> 代码块: `uTransition` + + +元素过渡动画 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-transition) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file