修复bug

master
fy 2026-02-04 20:59:33 +08:00
parent 6adf228d02
commit c2e2863bd2
200 changed files with 45 additions and 23478 deletions

View File

@ -48,7 +48,7 @@
},
"quickapp" : {},
"mp-weixin" : {
"appid" : "wxe94413d023e0e7df",
"appid" : "wx29a83645aa2b4ebd",
"setting" : {
"urlCheck" : false,
"es6" : false,

View File

@ -72,12 +72,6 @@
"navigationBarTitleText": "浏览文本"
}
},
{
"path": "pages/menu",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/user",
"style": {
@ -139,12 +133,6 @@
}
},
{
"path": "pages/expired/expired",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/batchDeleteProduct/batchDeleteProduct",
"style": {
@ -174,13 +162,8 @@
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/navailable/navailable",
"style": {
"navigationBarTitleText": ""
}
}
],
"tabBar": {
"color": "#000000",

View File

@ -473,7 +473,7 @@ export default {
brand: '那',
code: '1',
shelfCode: '',
productionDate: ''
productionDate: '',
},
priceOptions: [
{ price: 2.0, desc: '92%商家卖' },
@ -615,7 +615,8 @@ export default {
buyPrice: data.costPrice || this.goodsInfo.buyPrice,
category: data.classifcation || this.goodsInfo.category,
brand: data.productBrand || this.goodsInfo.brand,
code: data.productCode || this.goodsInfo.code
code: data.productCode || this.goodsInfo.code,
shelfCode:data.shelfCode || this.goodsInfo.shelfCode,
};
this.activePrice = data.storePrice || this.goodsInfo.salePrice;
@ -699,8 +700,8 @@ export default {
productCode: this.goodsInfo.code,
shelfCode: this.goodsInfo.shelfCode || '',
expirationManagement: this.switchStatus.expire ? 1 : 0,
shelfLife: this.switchStatus.expire ? this.expireSettings.days : null,
approaching: this.switchStatus.expire ? this.expireSettings.warnDays : null,
shelfLife: this.switchStatus.expire ? this.expireSettings.days : '',
approaching: this.switchStatus.expire ? this.expireSettings.warnDays : '',
productionDate: this.goodsInfo.productionDate || '',
storeId: storeId,
productBrand: this.goodsInfo.brand,

View File

@ -1,858 +0,0 @@
<template>
<view class="container">
<!-- 顶部状态栏/时间 -->
<view class="status-bar">
<text class="time">9:41</text>
</view>
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-input">
<uni-icons type="search" size="18" color="#999"></uni-icons>
<input
type="text"
placeholder="搜索商品名称、条码"
placeholder-class="placeholder"
v-model="searchText"
@confirm="onSearch"
/>
<uni-icons type="scan" size="18" color="#999"></uni-icons>
</view>
</view>
<!-- 筛选标签 -->
<view class="filter-tabs">
<scroll-view class="tabs-scroll" scroll-x="true">
<view
class="tab-item"
:class="{ active: currentTab === 'all' }"
@tap="switchTab('all')"
>
全部
</view>
<view
class="tab-item"
:class="{ active: currentTab === 'expiring' }"
@tap="navigateToExpiredPage"
>
临期/过期<text class="badge">0</text>
</view>
<view
class="tab-item"
:class="{ active: currentTab === 'outstock' }"
@tap="switchTab('outstock')"
>
缺货<text class="badge">0</text>
</view>
<view class="tab-item filter-btn" @tap="showFilter = !showFilter">
<text>筛选</text>
<uni-icons type="arrowdown" size="14" color="#666"></uni-icons>
</view>
</scroll-view>
</view>
<!-- 微店状态栏 -->
<view class="store-status">
<view class="status-left-content">
<!-- 快速处理卡片 -->
<view class="dui">
<image class="dui-img" src="http://193.112.94.36:8099/static/images/Mask group.png"></image>
</view>
<view class="status-left">
<text class="status-text">快速处理</text>
<text class="status-num">临期/过期食品</text>
</view>
</view>
<view class="status-right-buttons">
<!-- 临期促销按钮 -->
<view class="quick-btn pink-btn" @tap="goToPromotion">
<text class="btn-text">临期促销</text>
<uni-icons type="arrowright" size="12" color="#fff"></uni-icons>
</view>
<!-- 过期出库按钮 -->
<view class="quick-btn pink-btn" @tap="goToExpiredOut">
<text class="btn-text">过期出库</text>
<uni-icons type="arrowright" size="12" color="#fff"></uni-icons>
</view>
</view>
</view>
<!-- 商品列表 -->
<scroll-view class="goods-list" scroll-y="true">
<view class="goods-item" v-for="item in goodsList" :key="item.id">
<view class="goods-more-icon" @tap="toggleDeleteMenu(item)">
<uni-icons type="more-filled" size="18"></uni-icons>
</view>
<view class="goods-delete-menu" v-if="item.showDeleteMenu" @tap="showDeleteConfirm(item)">
<text class="delete-menu-text">删除商品</text>
</view>
<view class="goods-top" @tap="goToStockDetail(item)">
<view class="goods-tou">
<image :src="item.mainImage ? 'http://193.112.94.36:8081' + item.mainImage : '/static/687b6f95b14eff60f4b77147b3726ab2.jpg'"></image>
</view>
<view class="goods-info">
<text class="goods-name">{{ item.productName }}</text>
<text class="goods-barcode">{{ item.productBarCode }}</text>
<text class="goods-price">{{ item.storePrice ? item.storePrice.toFixed(2) : '0.00' }}</text>
</view>
</view>
<view class="goods-divider"></view>
<view class="goods-stock" >
<text class="stock-label">总库存</text>
<text class="stock-num">{{ item.stockQuantity }} </text>
<text class="stock-arrow">></text>
</view>
<!-- <view class="goods-actions">
<view class="action-btn add-btn" @tap="addGoodsStock(item)">
<text>添加商品/库存</text>
</view>
</view> -->
</view>
</scroll-view>
<!-- 底部操作按钮 -->
<view class="bottom-actions">
<view class="action-btn more-btn" @tap="showMoreActions(item)">
<text>更多</text>
</view>
<button class="action-button primary" @tap="addNewGoods">
添加商品/库存
</button>
<!-- <button class="action-button" @tap="batchOperation">
批量操作
</button> -->
</view>
<!-- 更多操作弹窗 -->
<uni-popup ref="popup" type="bottom">
<view class="popup-content">
<view class="popup-item" @tap="goToInventoryCount">
<uni-icons type="scan" size="20" color="#333"></uni-icons>
<text>盘点库存</text>
</view>
<view class="popup-item" @tap="goToOutbound">
<uni-icons type="arrowright" size="20" color="#333"></uni-icons>
<text>出库</text>
</view>
<view class="popup-item" @tap="batchDeleteGoods">
<uni-icons type="trash" size="20" color="#333"></uni-icons>
<text>批量删除商品</text>
</view>
<view class="popup-item" @tap="goToCategoryManagement">
<uni-icons type="apps" size="20" color="#333"></uni-icons>
<text>分类管理</text>
</view>
<view class="popup-item" @tap="goToBrandManagement">
<uni-icons type="navigate" size="20" color="#333"></uni-icons>
<text>品牌管理</text>
</view>
<view class="popup-item" @tap="goToBarcodeSettings">
<uni-icons type="settings" size="20" color="#333"></uni-icons>
<text>条码规则设置</text>
</view>
<view class="popup-item cancel" @tap="$refs.popup.close()">
<text>取消</text>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import { getProductList as fetchProductList, deleteProduct } from '@/api/product'
import { getToken, getStoreId } from '@/utils/auth'
export default {
data() {
return {
currentTab: 'expiring',
searchText: '',
showFilter: false,
selectedGoods: null,
storeId: null, // ID
goodsList: [
{
id: null,
productName: '',
productBarCode: '',
storePrice: 0.00,
stockQuantity: 0,
}
]
}
},
onLoad() {
// ID
const storeId = getStoreId();
if (storeId) {
this.storeId = storeId;
console.log('当前门店ID:', storeId);
} else {
uni.showToast({
title: '请先选择门店',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
return;
}
this.getProductList();
},
onShow() {
this.getProductList();
},
methods: {
async getProductList(searchParams = {}) {
try {
const token = getToken();
console.log('当前Token:', token);
console.log('Token是否存在:', !!token);
console.log('查询参数:', searchParams);
// ID
if (this.storeId) {
searchParams.storeId = this.storeId;
}
const res = await fetchProductList(searchParams);
console.log('商品列表接口返回:', res);
if (res && res.code === 200 && res.data) {
this.goodsList = res.data.map(item => ({
...item,
showDeleteMenu: false
}));
console.log('处理后的商品列表:', this.goodsList);
} else {
uni.showToast({
title: res?.msg || '获取商品列表失败',
icon: 'none'
});
}
} catch (error) {
console.error('获取商品列表失败:', error);
console.error('错误详情:', JSON.stringify(error));
uni.showToast({
title: '网络请求失败',
icon: 'none'
});
}
},
switchTab(tab) {
if (tab === 'all') {
//
uni.navigateTo({
url: `/pages/product/product?tab=${tab}`
});
} else if (tab === 'outstock') {
//
uni.navigateTo({
url: `/pages/navailable/navailable?tab=${tab}`
});
} else {
this.currentTab = tab;
//
console.log('切换到标签:', tab);
}
},
onSearch() {
console.log('搜索关键词:', this.searchText);
if (!this.searchText || this.searchText.trim() === '') {
this.getProductList();
return;
}
const searchText = this.searchText.trim();
let searchParams = {};
if (/^\d+$/.test(searchText)) {
searchParams = { productName: searchText };
console.log('商品名称模糊查询:', searchParams);
} else {
searchParams = { productBarCode: searchText };
console.log('按条形码精准查询按:', searchParams);
}
this.getProductList(searchParams);
},
goToSettings() {
uni.navigateTo({
url: '/pages/settings/index'
});
},
showMoreActions(goods) {
this.selectedGoods = goods;
this.$refs.popup.open();
},
addGoodsStock(goods) {
console.log('添加商品库存:', goods);
uni.showModal({
title: '添加库存',
content: `${goods.name} 添加库存`,
success: (res) => {
if (res.confirm) {
//
}
}
});
},
goToStockDetail(item) {
// ID
uni.navigateTo({
url: `/pages/edit/edit?id=${item.id}`
});
},
toggleDeleteMenu(item) {
item.showDeleteMenu = !item.showDeleteMenu;
},
showDeleteConfirm(item) {
uni.showModal({
title: '确认删除',
content: `确定要删除商品"${item.productName}"吗?`,
success: async (res) => {
if (res.confirm) {
try {
const result = await deleteProduct(item.id);
console.log('删除商品结果:', result);
if (result && result.code === 200) {
uni.showToast({
title: '删除成功',
icon: 'success'
});
//
this.getProductList();
} else {
uni.showToast({
title: result?.msg || '删除失败',
icon: 'none'
});
}
} catch (error) {
console.error('删除商品失败:', error);
uni.showToast({
title: '删除失败',
icon: 'none'
});
}
}
}
});
},
addNewGoods() {
uni.navigateTo({
url: '/pages/enter/enter'
// url: '/pages/addProduct/addProduct'
});
},
// /
navigateToExpiredPage() {
console.log('当前已是临期/过期商品页面');
},
batchOperation() {
uni.showActionSheet({
itemList: ['批量修改价格', '批量修改库存', '批量下架'],
success: (res) => {
console.log('选择了操作:', res.tapIndex);
}
});
},
editGoods(goods) {
this.$refs.popup.close();
uni.navigateTo({
url: `/pages/goods/edit?id=${goods.id}`
});
},
adjustStock(goods) {
this.$refs.popup.close();
uni.showModal({
title: '调整库存',
content: `调整 ${goods.name} 的库存数量`,
editable: true,
placeholderText: '请输入调整数量',
success: (res) => {
if (res.confirm && res.content) {
console.log('调整库存数量:', res.content);
}
}
});
},
deleteGoods(goods) {
this.$refs.popup.close();
uni.showModal({
title: '确认删除',
content: `确定要删除商品"${goods.name}"吗?`,
success: (res) => {
if (res.confirm) {
console.log('删除商品:', goods);
uni.showToast({
title: '删除成功',
icon: 'success'
});
}
}
});
},
//
batchDeleteGoods() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/batchDeleteProduct/batchDeleteProduct'
});
},
//
goToCategoryManagement() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/category/category'
});
},
//
goToBrandManagement() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/addBrand/addBrand'
});
},
//
goToBarcodeSettings() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/barcodeSettings/barcodeSettings'
});
},
//
goToInventoryCount() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/inventoryCount/inventoryCount'
});
},
//
goToOutbound() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/outbound/outbound'
});
}
}
}
</script>
<style scoped>
.container {
background-color: #f5f5f5;
min-height: 100vh;
}
.goods-tou{
width: 100rpx;
height: 100rpx;
margin-right: 16rpx;
flex-shrink: 0;
}
.goods-tou image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
/* 状态栏 */
.status-bar {
padding: 10px 16px;
background-color: #fff;
}
.time {
font-size: 16px;
font-weight: 600;
}
/* 快速操作整行容器 */
.quick-actions-bar {
display: flex;
align-items: center;
background: #fff;
padding: 12rpx 16rpx;
margin: 16rpx;
border-radius: 12rpx;
gap: 12rpx;
}
/* 快速处理卡片 */
.quick-card {
display: flex;
align-items: center;
background: #ff4d4f;
padding: 12rpx 20rpx;
border-radius: 30rpx;
color: #fff;
flex-shrink: 0;
}
.card-icon {
margin-right: 8rpx;
}
.icon-img {
width: 24rpx;
height: 24rpx;
}
.card-text {
display: flex;
flex-direction: column;
}
.main-text {
font-size: 26rpx;
font-weight: 500;
line-height: 1.2;
}
.sub-text {
font-size: 20rpx;
opacity: 0.9;
line-height: 1.2;
}
/* 搜索栏 */
.search-bar {
padding: 12px 16px;
background-color: #fff;
}
.search-input {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 8px;
padding: 10px 12px;
}
.search-input input {
flex: 1;
margin-left: 8px;
font-size: 14px;
}
.placeholder {
color: #999;
}
/* 筛选标签 */
.filter-tabs {
background-color: #fff;
padding: 12px 16px;
border-bottom: 1px solid #eee;
}
.tabs-scroll {
white-space: nowrap;
}
.tab-item {
display: inline-block;
padding: 6px 12px;
margin-right: 10px;
border-radius: 16px;
font-size: 14px;
background-color: #f5f5f5;
color: #666;
}
.tab-item.active {
background-color: #e8f4ff;
color: #007aff;
}
.badge {
margin-left: 4px;
color: #ff4444;
}
.filter-btn {
display: inline-flex;
align-items: center;
}
/* 微店状态栏 */
.store-status {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background-color: #fff;
border-radius: 20rpx;
margin-top: 8px;
border-bottom: 1px solid #eee;
}
.status-left-content {
display: flex;
align-items: center;
}
.status-right-buttons {
display: flex;
align-items: center;
gap: 12rpx;
}
.dui {
margin-right: 12rpx;
}
.dui-img {
width: 24rpx;
height: 24rpx;
}
.status-left {
display: flex;
flex-direction: column;
margin-left: 0;
}
.status-text {
font-size: 14px;
color: #333;
}
.status-num {
font-size: 12px;
color: #666;
margin-top: 4px;
}
.status-right {
display: flex;
align-items: center;
margin-right: 1rpx;
}
.set-text {
font-size: 14px;
color: #666;
margin-right: 4px;
}
/* 右侧粉色按钮通用样式 */
.quick-btn {
display: flex;
align-items: center;
background: red;
padding: 8rpx 16rpx;
border-radius: 20rpx;
color: white;
font-size: 22rpx;
gap: 4rpx;
flex-shrink: 0;
}
.btn-text {
font-size: 22rpx;
}
/* 商品列表 */
.goods-list {
height: calc(100vh - 320px);
padding: 16px;
}
.goods-item {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
position: relative;
}
.goods-more-icon {
position: absolute;
top: 12px;
right: 12px;
z-index: 10;
}
.goods-delete-menu {
position: absolute;
top: 40px;
right: 12px;
background-color: #fff;
border-radius: 8rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
padding: 12rpx 24rpx;
z-index: 9;
}
.delete-menu-text {
font-size: 28rpx;
color: #ff4444;
}
.goods-top {
display: flex;
align-items: flex-start;
margin-bottom: 12px;
}
.goods-info {
flex: 1;
margin-bottom: 0;
}
.goods-name {
display: block;
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 6px;
}
.goods-barcode {
font-size: 12px;
color: #999;
display: block;
margin-bottom: 8px;
}
.goods-price {
font-size: 18px;
color: #ff4444;
font-weight: 600;
}
.goods-divider {
height: 1px;
border-bottom: 1px dashed #e0e0e0;
margin-bottom: 12px;
}
.goods-stock {
display: flex;
align-items: center;
padding: 8px 0;
margin-bottom: 4px;
}
.stock-arrow {
font-size: 20px;
color: #999;
margin-left: auto;
}
.stock-label {
font-size: 14px;
color: #666;
}
.stock-num {
font-size: 16px;
color: #333;
font-weight: 500;
}
.goods-actions {
display: flex;
justify-content: space-between;
}
.action-btn {
padding: 8px 16px;
border-radius: 6px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
}
.more-btn {
background-color: #f5f5f5;
color: #666;
}
.add-btn {
background-color: #e8f4ff;
color: #007aff;
}
/* 底部操作按钮 */
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
padding: 16px;
background-color: #fff;
border-top: 1px solid #eee;
}
.action-button {
flex: 1;
margin: 0 8px;
padding: 12px;
border-radius: 8px;
font-size: 16px;
background-color: #f5f5f5;
color: #333;
border: none;
}
.action-button.primary {
background-color: #007aff;
color: #fff;
}
/* 弹窗样式 */
.popup-content {
background-color: #fff;
border-radius: 16px 16px 0 0;
padding: 20px;
}
.popup-item {
display: flex;
align-items: center;
padding: 16px 0;
border-bottom: 1px solid #f5f5f5;
font-size: 16px;
}
.popup-item:last-child {
border-bottom: none;
}
.popup-item text {
margin-left: 12px;
}
.popup-item.cancel {
justify-content: center;
color: #666;
}
</style>

View File

@ -45,14 +45,14 @@
</view>
<uni-icons type="checkbox-filled" size="18" color="#FF9900" v-if="selectedShop === 'enterprise'"></uni-icons>
</view>
<view class="create-btn">
<view class="create-btn" @click="createQi">
<uni-icons type="plusempty" size="16" ></uni-icons>
<text class="create-text">创建企业组织</text>
</view>
</view>
<!-- 底部创建店铺按钮 -->
<view class="bottom-create-btn">
<view class="bottom-create-btn" @click="createDian">
<uni-icons type="plusempty" size="16"></uni-icons>
<text class="create-text">创建店铺</text>
</view>
@ -85,7 +85,7 @@
src="http://193.112.94.36:8099/static/images/Frame 75.png"
mode="aspectFit"
class="close-icon"
@click="closeDuty"
/>
</view>
@ -196,61 +196,61 @@
</view>
<text class="item-text">服务费</text>
</view>
<view class="grid-item" @click="navigateTo('商品管理')">
<view class="grid-item" @click="navigateTo('门店信息')">
<view class="item-icon">
</view>
<text class="item-text">商品管理</text>
<text class="item-text">门店信息</text>
</view>
<view class="grid-item" @click="navigateTo('数据分析')">
<view class="grid-item" @click="navigateTo('24商城')">
<view class="item-iconb">
</view>
<text class="item-text">数据分析</text>
<text class="item-text">24商城</text>
</view>
<view class="grid-item" @click="navigateTo('订单管理')">
<view class="grid-item" @click="navigateTo('店铺保障')">
<view class="item-iconc">
</view>
<text class="item-text">订单管理</text>
<text class="item-text">店铺保障</text>
</view>
<view class="grid-item" @click="navigateTo('监控视频')">
<view class="grid-item" @click="navigateTo('店铺设备')">
<view class="item-icond">
</view>
<text class="item-text">监控视频</text>
<text class="item-text">店铺设备</text>
</view>
<view class="grid-item" @click="navigateTo('商品管理')">
<view class="grid-item" @click="navigateTo('值班留言')">
<view class="item-icon">
</view>
<text class="item-text">商品管理</text>
<text class="item-text">值班留言</text>
</view>
<view class="grid-item" @click="navigateTo('数据分析')">
<view class="grid-item" @click="navigateTo('活动报名')">
<view class="item-iconb">
</view>
<text class="item-text">数据分析</text>
<text class="item-text">活动报名</text>
</view>
<view class="grid-item" @click="navigateTo('订单管理')">
<view class="grid-item" @click="navigateTo('营销推广')">
<view class="item-iconc">
</view>
<text class="item-text">订单管理</text>
<text class="item-text">营销推广</text>
</view>
<view class="grid-item" @click="navigateTo('监控视频')">
<view class="grid-item" @click="navigateTo('全部客户')">
<view class="item-icond">
</view>
<text class="item-text">监控视频</text>
<text class="item-text">全部客户</text>
</view>
<view class="grid-item" @click="navigateTo('商品管理')">
<view class="grid-item" @click="navigateTo('安全卫士')">
<view class="item-icon">
</view>
<text class="item-text">商品管理</text>
<text class="item-text">安全卫士</text>
</view>
@ -322,6 +322,18 @@ export default {
// url: `/pages/${page}/index`
// })
},
createQi(){
uni.showToast({
title: `模块建设中~`,
icon: 'none'
})
},
createDian(){
uni.showToast({
title: `模块建设中~`,
icon: 'none'
})
},
goToUser() {
uni.navigateTo({
url: '/pages/user/user'
@ -1071,7 +1083,7 @@ export default {
.item-iconh {
width: 80rpx;
height: 80rpx;
background-image: url('http://193.112.94.36:8099/static/images/Frame 64.png');
background-image: url('http://193.112.94.36:8099/static/images/Frame 63 (1).png');
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;

View File

@ -1,109 +0,0 @@
<template>
<view class="menu">
<!-- 顶部用户信息 -->
<view class="user-info">
<image class="avatar" src="/static/avatar.png"></image>
<text class="name">个人店</text>
<text class="sub">商店智能店</text>
<text class="stats">已上线50万会员 | 管理员</text>
</view>
<!-- 企业管理 -->
<view class="section">
<text class="section-title">企业管理</text>
<view class="section-item">
<text class="item-label">企业名称</text>
<text class="item-value">总部/管理员</text>
</view>
<view class="section-item">
<text class="item-label">创建企业组织</text>
<text class="item-label">创建店铺</text>
</view>
</view>
<!-- 其他菜单项 -->
<view class="menu-list">
<view class="menu-item" @click="goToPage('shop')">
<text></text>
<text>202 今日进店</text>
</view>
<view class="menu-item" @click="goToPage('product')">
<text>商品管理</text>
</view>
<!-- 其他菜单项... -->
</view>
</view>
</template>
<script>
export default {
methods: {
goToPage(page) {
uni.navigateTo({ url: `/pages/${page}/${page}` })
this.$emit('close')
}
}
}
</script>
<style scoped>
.menu {
padding: 40rpx;
height: 100%;
background: #fff;
}
.user-info {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 40rpx;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
margin-bottom: 20rpx;
}
.name {
font-size: 36rpx;
font-weight: bold;
}
.sub {
font-size: 28rpx;
color: #666;
margin: 10rpx 0;
}
.stats {
font-size: 24rpx;
color: #888;
}
.section {
margin: 30rpx 0;
}
.section-title {
font-size: 30rpx;
font-weight: bold;
display: block;
margin-bottom: 20rpx;
}
.section-item {
display: flex;
justify-content: space-between;
margin: 15rpx 0;
}
.menu-item {
padding: 25rpx 0;
border-bottom: 1rpx solid #eee;
display: flex;
justify-content: space-between;
}
</style>

View File

@ -1,899 +0,0 @@
<template>
<view class="container">
<!-- 顶部状态栏/时间 -->
<view class="status-bar">
<text class="time">9:41</text>
</view>
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-input">
<uni-icons type="search" size="18" color="#999"></uni-icons>
<input
type="text"
placeholder="搜索商品名称、条码"
placeholder-class="placeholder"
v-model="searchText"
@confirm="onSearch"
<uni-icons type="scan" size="18" color="#999"></uni-icons>
</view>
</view>
<!-- 筛选标签 -->
<view class="filter-tabs">
<scroll-view class="tabs-scroll" scroll-x="true">
<view
class="tab-item"
:class="{ active: currentTab === 'all' }"
@tap="switchTab('all')"
>
全部
</view>
<view
class="tab-item"
:class="{ active: currentTab === 'expiring' }"
@tap="navigateToExpiredPage"
>
临期/过期<text class="badge">0</text>
</view>
<view
class="tab-item"
:class="{ active: currentTab === 'outstock' }"
@tap="switchTab('outstock')"
>
缺货<text class="badge">0</text>
</view>
<view class="tab-item filter-btn" @tap="showFilter = !showFilter">
<text>筛选</text>
<uni-icons type="arrowdown" size="14" color="#666"></uni-icons>
</view>
</scroll-view>
</view>
<!-- 库存不足预警设置 -->
<view class="store-status">
<view class="status-content">
<view class="dui">
<image class="dui-img" src="http://193.112.94.36:8099/static/images/Mask group (1).png"></image>
</view>
<view class="status-left">
<text class="status-text">库存不足预警设置</text>
<text class="status-num">库存低于已设置阈值时会收到APP推送提醒</text>
</view>
</view>
<view class="status-right" @tap="goToSettings">
<text class="set-text">设置</text>
<uni-icons type="forward" size="14" color="#666"></uni-icons>
</view>
</view>
<!-- 商品列表/空状态 -->
<scroll-view class="goods-list" scroll-y="true">
<view class="empty-state" v-if="goodsList.length === 0">
<image class="empty-img" src="/static/empty.png"></image>
<text class="empty-text">没有找到相关商品</text>
</view>
<view class="goods-item" v-for="item in goodsList" :key="item.id">
<view class="goods-more-icon" @tap="toggleDeleteMenu(item)">
<uni-icons type="more-filled" size="18"></uni-icons>
</view>
<view class="goods-delete-menu" v-if="item.showDeleteMenu" @tap="showDeleteConfirm(item)">
<text class="delete-menu-text">删除商品</text>
</view>
<view class="goods-top" @tap="goToStockDetail(item)">
<view class="goods-tou">
<image :src="item.mainImage ? 'http://193.112.94.36:8081' + item.mainImage : '/static/687b6f95b14eff60f4b77147b3726ab2.jpg'"></image>
</view>
<view class="goods-info">
<text class="goods-name">{{ item.productName }}</text>
<text class="goods-barcode">{{ item.productBarCode }}</text>
<text class="goods-price">{{ item.storePrice ? item.storePrice.toFixed(2) : '0.00' }}</text>
</view>
</view>
<view class="goods-divider"></view>
<view class="goods-stock" >
<text class="stock-label">总库存</text>
<text class="stock-num">{{ item.stockQuantity }} </text>
<text class="stock-arrow">></text>
</view>
</view>
</scroll-view>
<!-- 底部操作按钮 -->
<view class="bottom-actions">
<view class="action-btn more-btn" @tap="showMoreActions()">
<text>更多</text>
</view>
<button class="action-button primary" @tap="addNewGoods">
添加商品/库存
</button>
<!-- <button class="action-button" @tap="batchOperation">
批量操作
</button> -->
</view>
<!-- 更多操作弹窗 -->
<uni-popup ref="popup" type="bottom">
<view class="popup-content">
<view class="popup-item" @tap="goToInventoryCount">
<uni-icons type="scan" size="20" color="#333"></uni-icons>
<text>盘点库存</text>
</view>
<view class="popup-item" @tap="goToOutbound">
<uni-icons type="arrowright" size="20" color="#333"></uni-icons>
<text>出库</text>
</view>
<view class="popup-item" @tap="batchDeleteGoods">
<uni-icons type="trash" size="20" color="#333"></uni-icons>
<text>批量删除商品</text>
</view>
<view class="popup-item" @tap="goToCategoryManagement">
<uni-icons type="apps" size="20" color="#333"></uni-icons>
<text>分类管理</text>
</view>
<view class="popup-item" @tap="goToBrandManagement">
<uni-icons type="navigate" size="20" color="#333"></uni-icons>
<text>品牌管理</text>
</view>
<view class="popup-item" @tap="goToBarcodeSettings">
<uni-icons type="settings" size="20" color="#333"></uni-icons>
<text>条码规则设置</text>
</view>
<view class="popup-item cancel" @tap="$refs.popup.close()">
<text>取消</text>
</view>
</view>
</uni-popup>
<!-- 更多操作弹窗 -->
<!-- <uni-popup ref="popup" type="bottom">
<view class="popup-content">
<view class="popup-item" @tap="editGoods(selectedGoods)">
<uni-icons type="compose" size="20" color="#333"></uni-icons>
<text>编辑商品</text>
</view>
<view class="popup-item" @tap="adjustStock(selectedGoods)">
<uni-icons type="plus" size="20" color="#333"></uni-icons>
<text>调整库存</text>
</view>
<view class="popup-item" @tap="deleteGoods(selectedGoods)">
<uni-icons type="trash" size="20" color="#ff4444"></uni-icons>
<text style="color: #ff4444;">删除商品</text>
</view>
<view class="popup-item cancel" @tap="$refs.popup.close()">
<text>取消</text>
</view>
</view>
</uni-popup> -->
</view>
</template>
<script>
import { getProductList as fetchProductList, deleteProduct } from '@/api/product'
import { getToken, getStoreId } from '@/utils/auth'
export default {
data() {
return {
currentTab: 'expiring',
searchText: '',
showFilter: false,
selectedGoods: null,
storeId: null, // ID
goodsList: []
}
},
onLoad(options) {
// ID
const storeId = getStoreId();
if (storeId) {
this.storeId = storeId;
console.log('当前门店ID:', storeId);
} else {
uni.showToast({
title: '请先选择门店',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
return;
}
// tab
if (options && options.tab) {
this.currentTab = options.tab;
}
this.getProductList();
},
onShow() {
this.getProductList();
},
methods: {
async getProductList(searchParams = {}) {
try {
const token = getToken();
console.log('当前Token:', token);
console.log('Token是否存在:', !!token);
console.log('查询参数:', searchParams);
// ID
if (this.storeId) {
searchParams.storeId = this.storeId;
}
//
if (this.currentTab === 'outstock') {
// stockQuantity=0
// 使
searchParams.stockQuantity = 0;
}
const res = await fetchProductList(searchParams);
console.log('商品列表接口返回:', res);
if (res && res.code === 200 && res.data) {
this.goodsList = res.data.map(item => ({
...item,
showDeleteMenu: false
}));
console.log('处理后的商品列表:', this.goodsList);
} else {
this.goodsList = [];
uni.showToast({
title: res?.msg || '获取商品列表失败',
icon: 'none'
});
}
} catch (error) {
this.goodsList = [];
console.error('获取商品列表失败:', error);
console.error('错误详情:', JSON.stringify(error));
uni.showToast({
title: '网络请求失败',
icon: 'none'
});
}
},
switchTab(tab) {
if (tab === 'all') {
//
uni.navigateTo({
url: `/pages/product/product?tab=${tab}`
});
} else if (tab === 'outstock') {
//
uni.navigateTo({
url: `/pages/navailable/navailable?tab=${tab}`
});
} else {
this.currentTab = tab;
//
console.log('切换到标签:', tab);
}
},
//
batchDeleteGoods() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/batchDeleteProduct/batchDeleteProduct'
});
},
//
goToCategoryManagement() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/category/category'
});
},
//
goToBrandManagement() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/addBrand/addBrand'
});
},
//
goToBarcodeSettings() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/barcodeSettings/barcodeSettings'
});
},
//
goToInventoryCount() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/inventoryCount/inventoryCount'
});
},
//
goToOutbound() {
this.$refs.popup.close();
uni.navigateTo({
url: '/pages/outbound/outbound'
});
},
onSearch() {
console.log('搜索关键词:', this.searchText);
if (!this.searchText || this.searchText.trim() === '') {
this.getProductList();
return;
}
const searchText = this.searchText.trim();
let searchParams = {};
if (/^\d+$/.test(searchText)) {
searchParams = { productBarCode: searchText };
console.log('按条形码精准查询:', searchParams);
} else {
searchParams = { productName: searchText };
console.log('商品名称模糊查询:', searchParams);
}
this.getProductList(searchParams);
},
goToAlertSettings() {
uni.navigateTo({
url: '/pages/alertSettings/index'
});
},
showMoreActions() {
this.$refs.popup.open();
},
goToStockTake() {
this.$refs.moreMenu.close();
uni.navigateTo({
url: '/pages/stockTake/index'
});
},
goToOutbound() {
this.$refs.moreMenu.close();
uni.navigateTo({
url: '/pages/outbound/index'
});
},
batchDelete() {
this.$refs.moreMenu.close();
uni.showModal({
title: '批量删除',
content: '确定要批量删除商品吗?',
success: (res) => {
if (res.confirm) {
//
}
}
});
},
goToCategory() {
this.$refs.moreMenu.close();
uni.navigateTo({
url: '/pages/category/index'
});
},
goToBrand() {
this.$refs.moreMenu.close();
uni.navigateTo({
url: '/pages/brand/index'
});
},
goToBarcodeRule() {
this.$refs.moreMenu.close();
uni.navigateTo({
url: '/pages/barcodeRule/index'
});
},
goToStockDetail(item) {
// ID
uni.navigateTo({
url: `/pages/edit/edit?id=${item.id}`
});
},
toggleDeleteMenu(item) {
item.showDeleteMenu = !item.showDeleteMenu;
},
showDeleteConfirm(item) {
uni.showModal({
title: '确认删除',
content: `确定要删除商品"${item.productName}"吗?`,
success: async (res) => {
if (res.confirm) {
try {
const result = await deleteProduct(item.id);
console.log('删除商品结果:', result);
if (result && result.code === 200) {
uni.showToast({
title: '删除成功',
icon: 'success'
});
//
this.getProductList();
} else {
uni.showToast({
title: result?.msg || '删除失败',
icon: 'none'
});
}
} catch (error) {
console.error('删除商品失败:', error);
uni.showToast({
title: '删除失败',
icon: 'none'
});
}
}
}
});
},
addNewGoods() {
uni.navigateTo({
url: '/pages/enter/enter'
});
},
// /
navigateToExpiredPage() {
uni.navigateTo({
url: '/pages/expired/expired'
});
},
editGoods(goods) {
this.$refs.popup.close();
uni.navigateTo({
url: `/pages/goods/edit?id=${goods.id}`
});
},
adjustStock(goods) {
this.$refs.popup.close();
uni.showModal({
title: '调整库存',
content: `调整 ${goods.productName} 的库存数量`,
editable: true,
placeholderText: '请输入调整数量',
success: (res) => {
if (res.confirm && res.content) {
console.log('调整库存数量:', res.content);
}
}
});
},
deleteGoods(goods) {
this.$refs.popup.close();
uni.showModal({
title: '确认删除',
content: `确定要删除商品"${goods.productName}"吗?`,
success: (res) => {
if (res.confirm) {
console.log('删除商品:', goods);
uni.showToast({
title: '删除成功',
icon: 'success'
});
}
}
});
}
}
}
</script>
<style scoped>
.container {
background-color: #f5f5f5;
min-height: 100vh;
}
.goods-tou{
width: 100rpx;
height: 100rpx;
margin-right: 16rpx;
flex-shrink: 0;
}
.dui-img {
width: 24rpx;
height: 24rpx;
}
.goods-tou image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
/* 状态栏 */
.status-bar {
padding: 10px 16px;
background-color: #fff;
}
.time {
font-size: 16px;
font-weight: 600;
}
/* 搜索栏 */
.search-bar {
padding: 12px 16px;
background-color: #fff;
}
.search-input {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 8px;
padding: 10px 12px;
}
.search-input input {
flex: 1;
margin-left: 8px;
font-size: 14px;
}
.placeholder {
color: #999;
}
/* 筛选标签 */
.filter-tabs {
background-color: #fff;
padding: 12px 16px;
border-bottom: 1px solid #eee;
}
.tabs-scroll {
white-space: nowrap;
}
.tab-item {
display: inline-block;
padding: 6px 12px;
margin-right: 10px;
border-radius: 16px;
font-size: 14px;
background-color: #f5f5f5;
color: #666;
}
.tab-item.active {
background-color: #e8f4ff;
color: #007aff;
}
.badge {
margin-left: 4px;
color: #ff4444;
}
.filter-btn {
display: inline-flex;
align-items: center;
}
/* 库存不足预警栏 */
.alert-bar {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #E8F4FF;
padding: 12px 16px;
margin: 8px 16px;
border-radius: 8px;
}
.alert-left {
display: flex;
align-items: flex-start;
}
.alert-text-group {
margin-left: 8px;
}
.alert-text {
display: block;
font-size: 14px;
color: #333;
font-weight: 500;
}
.alert-desc {
display: block;
font-size: 12px;
color: #666;
margin-top: 2px;
}
.alert-right {
display: flex;
align-items: center;
}
.setting-text {
font-size: 14px;
color: #007AFF;
margin-right: 4px;
}
/* 商品列表 */
.goods-list {
height: calc(100vh - 320px);
padding: 16px;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
}
.empty-img {
width: 120rpx;
height: 120rpx;
margin-bottom: 16rpx;
}
.empty-text {
font-size: 14px;
color: #999;
}
.goods-item {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
position: relative;
}
.goods-more-icon {
position: absolute;
top: 12px;
right: 12px;
z-index: 10;
}
.goods-delete-menu {
position: absolute;
top: 40px;
right: 12px;
background-color: #fff;
border-radius: 8rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
padding: 12rpx 24rpx;
z-index: 9;
}
.delete-menu-text {
font-size: 28rpx;
color: #ff4444;
}
.goods-top {
display: flex;
align-items: flex-start;
margin-bottom: 12px;
}
.goods-info {
flex: 1;
margin-bottom: 0;
}
.goods-name {
display: block;
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 6px;
}
.goods-barcode {
font-size: 12px;
color: #999;
display: block;
margin-bottom: 8px;
}
.goods-price {
font-size: 18px;
color: #ff4444;
font-weight: 600;
}
.goods-divider {
height: 1px;
border-bottom: 1px dashed #e0e0e0;
margin-bottom: 12px;
}
.goods-stock {
display: flex;
align-items: center;
padding: 8px 0;
margin-bottom: 4px;
}
.stock-arrow {
font-size: 20px;
color: #999;
margin-left: auto;
}
.stock-label {
font-size: 14px;
color: #666;
}
.stock-num {
font-size: 16px;
color: #333;
font-weight: 500;
}
/* 底部操作按钮 */
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
padding: 16px;
background-color: #fff;
border-top: 1px solid #eee;
}
.action-btn {
padding: 8px 16px;
border-radius: 6px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
}
.more-btn {
background-color: #f5f5f5;
color: #666;
margin-right: 8px;
}
.action-button {
flex: 1;
margin: 0 8px;
padding: 12px;
border-radius: 8px;
font-size: 16px;
background-color: #f5f5f5;
color: #333;
border: none;
}
.action-button.primary {
background-color: #007aff;
color: #fff;
}
/* 左侧菜单样式 */
.menu-content {
background-color: #fff;
height: 100vh;
width: 280rpx;
padding: 20rpx 0;
}
.menu-item {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
font-size: 14px;
border-bottom: 1px solid #f5f5f5;
}
.menu-item text {
margin-left: 12rpx;
}
/* 弹窗样式 */
.popup-content {
background-color: #fff;
border-radius: 16px 16px 0 0;
padding: 20px;
}
.popup-item {
display: flex;
align-items: center;
padding: 16px 0;
border-bottom: 1px solid #f5f5f5;
font-size: 16px;
}
.popup-item:last-child {
border-bottom: none;
}
.popup-item text {
margin-left: 12px;
}
.popup-item.cancel {
justify-content: center;
color: #666;
}
/* 微店状态栏 */
.store-status {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background-color: #fff;
border-radius: 20rpx;
margin-top: 8px;
}
.status-content {
display: flex;
align-items: center;
}
.dui {
margin-right: 12rpx;
}
.dui-img {
width: 24rpx;
height: 24rpx;
}
.status-left {
display: flex;
flex-direction: column;
margin-left: 0;
}
.status-text {
font-size: 14px;
color: #333;
}
.status-num {
font-size: 12px;
color: #666;
margin-top: 4px;
}
.status-right {
display: flex;
align-items: center;
margin-right: 1rpx;
}
.set-text {
font-size: 14px;
color: #666;
margin-right: 4px;
}
</style>

View File

@ -133,12 +133,6 @@
<text class="stock-num">{{ item.stockQuantity }} </text>
<text class="stock-arrow">></text>
</view>
<!-- <view class="goods-actions">
<view class="action-btn add-btn" @tap="addGoodsStock(item)">
<text>添加商品/库存</text>
</view>
</view> -->
</view>
</scroll-view>
@ -150,9 +144,7 @@
<button class="action-button primary" @tap="addNewGoods">
添加商品/库存
</button>
<!-- <button class="action-button" @tap="batchOperation">
批量操作
</button> -->
</view>
<!-- 更多操作弹窗 -->
@ -411,7 +403,7 @@
addNewGoods() {
uni.navigateTo({
url: '/pages/enter/enter'
url: '/pages/addProduct/addProduct'
});
},

View File

@ -95,16 +95,6 @@ export default {
duration: 1000
});
//
// switch(pageName) {
// case '':
// uni.navigateTo({ url: '/pages/user/profile' });
// break;
// case '':
// uni.navigateTo({ url: '/pages/shop/info' });
// break;
// // ...
// }
},
handleLogout() {
this.$modal.confirm('确定注销并退出系统吗?').then(() => {

View File

@ -2,7 +2,7 @@
"libVersion": "3.14.0",
"projectname": "RuoYi-App-master",
"setting": {
"urlCheck": true,
"urlCheck": false,
"coverView": true,
"lazyloadPlaceholderEnable": false,
"skylineRenderEnable": false,

View File

@ -1,6 +0,0 @@
## 0.1.22022-06-08
- 修复 微信小程序 separator 不显示的Bug
## 0.1.12022-06-02
- 新增 支持 uni.scss 修改颜色
## 0.1.02022-04-21
- 初始化

View File

@ -1,121 +0,0 @@
<template>
<view class="uni-breadcrumb-item">
<view :class="{
'uni-breadcrumb-item--slot': true,
'uni-breadcrumb-item--slot-link': to && currentPage !== to
}" @click="navTo">
<slot />
</view>
<i v-if="separatorClass" class="uni-breadcrumb-item--separator" :class="separatorClass" />
<text v-else class="uni-breadcrumb-item--separator">{{ separator }}</text>
</view>
</template>
<script>
/**
* BreadcrumbItem 面包屑导航子组件
* @property {String/Object} to 路由跳转页面路径/对象
* @property {Boolean} replace 在使用 to 进行路由跳转时启用 replace 将不会向 history 添加新记录( h5 支持
*/
export default {
data() {
return {
currentPage: ""
}
},
options: {
virtualHost: true
},
props: {
to: {
type: String,
default: ''
},
replace:{
type: Boolean,
default: false
}
},
inject: {
uniBreadcrumb: {
from: "uniBreadcrumb",
default: null
}
},
created(){
const pages = getCurrentPages()
const page = pages[pages.length-1]
if(page){
this.currentPage = `/${page.route}`
}
},
computed: {
separator() {
return this.uniBreadcrumb.separator
},
separatorClass() {
return this.uniBreadcrumb.separatorClass
}
},
methods: {
navTo() {
const { to } = this
if (!to || this.currentPage === to){
return
}
if(this.replace){
uni.redirectTo({
url:to
})
}else{
uni.navigateTo({
url:to
})
}
}
}
}
</script>
<style lang="scss">
$uni-primary: #2979ff !default;
$uni-base-color: #6a6a6a !default;
$uni-main-color: #3a3a3a !default;
.uni-breadcrumb-item {
display: flex;
align-items: center;
white-space: nowrap;
font-size: 14px;
&--slot {
color: $uni-base-color;
padding: 0 10px;
&-link {
color: $uni-main-color;
font-weight: bold;
/* #ifndef APP-NVUE */
cursor: pointer;
/* #endif */
&:hover {
color: $uni-primary;
}
}
}
&--separator {
font-size: 12px;
color: $uni-base-color;
}
&:first-child &--slot {
padding-left: 0;
}
&:last-child &--separator {
display: none;
}
}
</style>

View File

@ -1,41 +0,0 @@
<template>
<view class="uni-breadcrumb">
<slot />
</view>
</template>
<script>
/**
* Breadcrumb 面包屑导航父组件
* @description 显示当前页面的路径快速返回之前的任意页面
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
* @property {String} separator 分隔符默认为斜杠'/'
* @property {String} separatorClass 图标分隔符 class
*/
export default {
options: {
virtualHost: true
},
props: {
separator: {
type: String,
default: '/'
},
separatorClass: {
type: String,
default: ''
}
},
provide() {
return {
uniBreadcrumb: this
}
}
}
</script>
<style lang="scss">
.uni-breadcrumb {
display: flex;
}
</style>

View File

@ -1,88 +0,0 @@
{
"id": "uni-breadcrumb",
"displayName": "uni-breadcrumb 面包屑",
"version": "0.1.2",
"description": "Breadcrumb 面包屑",
"keywords": [
"uni-breadcrumb",
"breadcrumb",
"uni-ui",
"面包屑导航",
"面包屑"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -1,66 +0,0 @@
## breadcrumb 面包屑导航
> **组件名uni-breadcrumb**
> 代码块: `ubreadcrumb`
显示当前页面的路径,快速返回之前的任意页面。
### 安装方式
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
### 基本用法
在 ``template`` 中使用组件
```html
<uni-breadcrumb separator="/">
<uni-breadcrumb-item v-for="(route,index) in routes" :key="index" :to="route.to">{{route.name}}</uni-breadcrumb-item>
</uni-breadcrumb>
```
```js
export default {
name: "uni-stat-breadcrumb",
data() {
return {
routes: [{
to: '/A',
name: 'A页面'
}, {
to: '/B',
name: 'B页面'
}, {
to: '/C',
name: 'C页面'
}]
};
}
}
```
## API
### Breadcrumb Props
|属性名 |类型 |默认值 |说明 |
|:-: |:-: |:-: |:-: |
|separator |String |斜杠'/' |分隔符 |
|separatorClass |String | |图标分隔符 class |
### Breadcrumb Item Props
|属性名 |类型 |默认值 |说明 |
|:-: |:-: |:-: |:-: |
|to |String | |路由跳转页面路径 |
|replace|Boolean | |在使用 to 进行路由跳转时,启用 replace 将不会向 history 添加新记录(仅 h5 支持) |
## 组件示例
点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/breadcrumb/breadcrumb](https://hellouniapp.dcloud.net.cn/pages/extUI/breadcrumb/breadcrumb)

View File

@ -1,30 +0,0 @@
## 1.4.122024-09-21
- 修复 calendar在选择日期范围后重新选择日期需要点两次的Bug
## 1.4.112024-01-10
- 修复 回到今天时,月份显示不一致问题
## 1.4.102023-04-10
- 修复 某些情况 monthSwitch 未触发的Bug
## 1.4.92023-02-02
- 修复 某些情况切换月份错误的Bug
## 1.4.82023-01-30
- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/161964)
## 1.4.72022-09-16
- 优化 支持使用 uni-scss 控制主题色
## 1.4.62022-09-08
- 修复 表头年月切换导致改变当前日期为选择月1号且未触发change事件的Bug
## 1.4.52022-02-25
- 修复 条件编译 nvue 不支持的 css 样式的Bug
## 1.4.42022-02-25
- 修复 条件编译 nvue 不支持的 css 样式的Bug
## 1.4.32021-09-22
- 修复 startDate、 endDate 属性失效的Bug
## 1.4.22021-08-24
- 新增 支持国际化
## 1.4.12021-08-05
- 修复 弹出层被 tabbar 遮盖的Bug
## 1.4.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.3.162021-05-12
- 新增 组件示例地址
## 1.3.152021-02-04
- 调整为uni_modules目录规范

View File

@ -1,544 +0,0 @@
/**
* @1900-2100区间内的公历农历互转
* @charset UTF-8
* @github https://github.com/jjonline/calendar.js
* @Author Jea杨(JJonline@JJonline.Cn)
* @Time 2014-7-21
* @Time 2016-8-13 Fixed 2033hexAttribution Annals
* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
* @Version 1.0.3
* @公历转农历calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
* @农历转公历calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
*/
/* eslint-disable */
var calendar = {
/**
* 农历1900-2100的润大小信息表
* @Array Of Property
* @return Hex
*/
lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
/** Add By JJonline@JJonline.Cn**/
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
0x0d520], // 2100
/**
* 公历每个月份的天数普通表
* @Array Of Property
* @return Number
*/
solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
/**
* 天干地支之天干速查表
* @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
* @return Cn string
*/
Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
/**
* 天干地支之地支速查表
* @Array Of Property
* @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
* @return Cn string
*/
Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
/**
* 天干地支之地支速查表<=>生肖
* @Array Of Property
* @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
* @return Cn string
*/
Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
/**
* 24节气速查表
* @Array Of Property
* @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
* @return Cn string
*/
solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
/**
* 1900-2100各年的24节气日期速查表
* @Array Of Property
* @return 0x string For splice
*/
sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
'97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
'97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
'97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
'97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
'9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
'7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
'977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
'7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
'7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
'665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
/**
* 数字转中文速查表
* @Array Of Property
* @trans ['日','一','二','三','四','五','六','七','八','九','十']
* @return Cn string
*/
nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
/**
* 日期转农历称呼速查表
* @Array Of Property
* @trans ['初','十','廿','卅']
* @return Cn string
*/
nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
/**
* 月份转农历称呼速查表
* @Array Of Property
* @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
* @return Cn string
*/
nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
/**
* 返回农历y年一整年的总天数
* @param lunar Year
* @return Number
* @eg:var count = calendar.lYearDays(1987) ;//count=387
*/
lYearDays: function (y) {
var i; var sum = 348
for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
return (sum + this.leapDays(y))
},
/**
* 返回农历y年闰月是哪个月若y年没有闰月 则返回0
* @param lunar Year
* @return Number (0-12)
* @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
*/
leapMonth: function (y) { // 闰字编码 \u95f0
return (this.lunarInfo[y - 1900] & 0xf)
},
/**
* 返回农历y年闰月的天数 若该年没有闰月则返回0
* @param lunar Year
* @return Number (02930)
* @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
*/
leapDays: function (y) {
if (this.leapMonth(y)) {
return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
}
return (0)
},
/**
* 返回农历y年m月非闰月的总天数计算m为闰月时的天数请使用leapDays方法
* @param lunar Year
* @return Number (-12930)
* @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
*/
monthDays: function (y, m) {
if (m > 12 || m < 1) { return -1 }// 月份参数从1至12参数错误返回-1
return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
},
/**
* 返回公历(!)y年m月的天数
* @param solar Year
* @return Number (-128293031)
* @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
*/
solarDays: function (y, m) {
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var ms = m - 1
if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
} else {
return (this.solarMonth[ms])
}
},
/**
* 农历年份转换为干支纪年
* @param lYear 农历年的年份数
* @return Cn string
*/
toGanZhiYear: function (lYear) {
var ganKey = (lYear - 3) % 10
var zhiKey = (lYear - 3) % 12
if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
},
/**
* 公历月日判断所属星座
* @param cMonth [description]
* @param cDay [description]
* @return Cn string
*/
toAstro: function (cMonth, cDay) {
var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
},
/**
* 传入offset偏移量返回干支
* @param offset 相对甲子的偏移量
* @return Cn string
*/
toGanZhi: function (offset) {
return this.Gan[offset % 10] + this.Zhi[offset % 12]
},
/**
* 传入公历(!)y年获得该年第n个节气的公历日期
* @param y公历年(1900-2100)n二十四节气中的第几个节气(1~24)从n=1(小寒)算起
* @return day Number
* @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
*/
getTerm: function (y, n) {
if (y < 1900 || y > 2100) { return -1 }
if (n < 1 || n > 24) { return -1 }
var _table = this.sTermInfo[y - 1900]
var _info = [
parseInt('0x' + _table.substr(0, 5)).toString(),
parseInt('0x' + _table.substr(5, 5)).toString(),
parseInt('0x' + _table.substr(10, 5)).toString(),
parseInt('0x' + _table.substr(15, 5)).toString(),
parseInt('0x' + _table.substr(20, 5)).toString(),
parseInt('0x' + _table.substr(25, 5)).toString()
]
var _calday = [
_info[0].substr(0, 1),
_info[0].substr(1, 2),
_info[0].substr(3, 1),
_info[0].substr(4, 2),
_info[1].substr(0, 1),
_info[1].substr(1, 2),
_info[1].substr(3, 1),
_info[1].substr(4, 2),
_info[2].substr(0, 1),
_info[2].substr(1, 2),
_info[2].substr(3, 1),
_info[2].substr(4, 2),
_info[3].substr(0, 1),
_info[3].substr(1, 2),
_info[3].substr(3, 1),
_info[3].substr(4, 2),
_info[4].substr(0, 1),
_info[4].substr(1, 2),
_info[4].substr(3, 1),
_info[4].substr(4, 2),
_info[5].substr(0, 1),
_info[5].substr(1, 2),
_info[5].substr(3, 1),
_info[5].substr(4, 2)
]
return parseInt(_calday[n - 1])
},
/**
* 传入农历数字月份返回汉语通俗表示法
* @param lunar month
* @return Cn string
* @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
*/
toChinaMonth: function (m) { // 月 => \u6708
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var s = this.nStr3[m - 1]
s += '\u6708'// 加上月字
return s
},
/**
* 传入农历日期数字返回汉字表示法
* @param lunar day
* @return Cn string
* @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
*/
toChinaDay: function (d) { // 日 => \u65e5
var s
switch (d) {
case 10:
s = '\u521d\u5341'; break
case 20:
s = '\u4e8c\u5341'; break
case 30:
s = '\u4e09\u5341'; break
default :
s = this.nStr2[Math.floor(d / 10)]
s += this.nStr1[d % 10]
}
return (s)
},
/**
* 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是立春
* @param y year
* @return Cn string
* @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
*/
getAnimal: function (y) {
return this.Animals[(y - 4) % 12]
},
/**
* 传入阳历年月日获得详细的公历农历object信息 <=>JSON
* @param y solar year
* @param m solar month
* @param d solar day
* @return JSON object
* @eg:console.log(calendar.solar2lunar(1987,11,01));
*/
solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
// 年份限定、上限
if (y < 1900 || y > 2100) {
return -1// undefined转换为数字变为NaN
}
// 公历传参最下限
if (y == 1900 && m == 1 && d < 31) {
return -1
}
// 未传参 获得当天
if (!y) {
var objDate = new Date()
} else {
var objDate = new Date(y, parseInt(m) - 1, d)
}
var i; var leap = 0; var temp = 0
// 修正ymd参数
var y = objDate.getFullYear()
var m = objDate.getMonth() + 1
var d = objDate.getDate()
var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
for (i = 1900; i < 2101 && offset > 0; i++) {
temp = this.lYearDays(i)
offset -= temp
}
if (offset < 0) {
offset += temp; i--
}
// 是否今天
var isTodayObj = new Date()
var isToday = false
if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
isToday = true
}
// 星期几
var nWeek = objDate.getDay()
var cWeek = this.nStr1[nWeek]
// 数字表示周几顺应天朝周一开始的惯例
if (nWeek == 0) {
nWeek = 7
}
// 农历年
var year = i
var leap = this.leapMonth(i) // 闰哪个月
var isLeap = false
// 效验闰月
for (i = 1; i < 13 && offset > 0; i++) {
// 闰月
if (leap > 0 && i == (leap + 1) && isLeap == false) {
--i
isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
} else {
temp = this.monthDays(year, i)// 计算农历普通月天数
}
// 解除闰月
if (isLeap == true && i == (leap + 1)) { isLeap = false }
offset -= temp
}
// 闰月导致数组下标重叠取反
if (offset == 0 && leap > 0 && i == leap + 1) {
if (isLeap) {
isLeap = false
} else {
isLeap = true; --i
}
}
if (offset < 0) {
offset += temp; --i
}
// 农历月
var month = i
// 农历日
var day = offset + 1
// 天干地支处理
var sm = m - 1
var gzY = this.toGanZhiYear(year)
// 当月的两个节气
// bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
// 依据12节气修正干支月
var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
if (d >= firstNode) {
gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
}
// 传入的日期的节气与否
var isTerm = false
var Term = null
if (firstNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 2]
}
if (secondNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 1]
}
// 日柱 当月一日与 1900/1/1 相差天数
var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
var gzD = this.toGanZhi(dayCyclical + d - 1)
// 该日期所属的星座
var astro = this.toAstro(m, d)
return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
},
/**
* 传入农历年月日以及传入的月份是否闰月获得详细的公历农历object信息 <=>JSON
* @param y lunar year
* @param m lunar month
* @param d lunar day
* @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
* @return JSON object
* @eg:console.log(calendar.lunar2solar(1987,9,10));
*/
lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
var isLeapMonth = !!isLeapMonth
var leapOffset = 0
var leapMonth = this.leapMonth(y)
var leapDay = this.leapDays(y)
if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
var day = this.monthDays(y, m)
var _day = day
// bugFix 2016-9-25
// if month is leap, _day use leapDays method
if (isLeapMonth) {
_day = this.leapDays(y, m)
}
if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
// 计算农历的时间差
var offset = 0
for (var i = 1900; i < y; i++) {
offset += this.lYearDays(i)
}
var leap = 0; var isAdd = false
for (var i = 1; i < m; i++) {
leap = this.leapMonth(y)
if (!isAdd) { // 处理闰月
if (leap <= i && leap > 0) {
offset += this.leapDays(y); isAdd = true
}
}
offset += this.monthDays(y, i)
}
// 转换闰月农历 需补充该年闰月的前一个月的时差
if (isLeapMonth) { offset += day }
// 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
var calObj = new Date((offset + d - 31) * 86400000 + stmap)
var cY = calObj.getUTCFullYear()
var cM = calObj.getUTCMonth() + 1
var cD = calObj.getUTCDate()
return this.solar2lunar(cY, cM, cD)
}
}
export default calendar

View File

@ -1,12 +0,0 @@
{
"uni-calender.ok": "ok",
"uni-calender.cancel": "cancel",
"uni-calender.today": "today",
"uni-calender.MON": "MON",
"uni-calender.TUE": "TUE",
"uni-calender.WED": "WED",
"uni-calender.THU": "THU",
"uni-calender.FRI": "FRI",
"uni-calender.SAT": "SAT",
"uni-calender.SUN": "SUN"
}

View File

@ -1,8 +0,0 @@
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
}

View File

@ -1,12 +0,0 @@
{
"uni-calender.ok": "确定",
"uni-calender.cancel": "取消",
"uni-calender.today": "今日",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

View File

@ -1,12 +0,0 @@
{
"uni-calender.ok": "確定",
"uni-calender.cancel": "取消",
"uni-calender.today": "今日",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

View File

@ -1,187 +0,0 @@
<template>
<view class="uni-calendar-item__weeks-box" :class="{
'uni-calendar-item--disable':weeks.disable,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
}"
@click="choiceDate(weeks)">
<view class="uni-calendar-item__weeks-box-item">
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text" :class="{
'uni-calendar-item--isDay-text': weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.date}}</text>
<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
}">{{todayText}}</text>
<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--extra':weeks.extraInfo.info,
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.extraInfo.info}}</text>
</view>
</view>
</template>
<script>
import { initVueI18n } from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const { t } = initVueI18n(i18nMessages)
export default {
emits:['change'],
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
}
},
computed: {
todayText() {
return t("uni-calender.today")
},
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
}
}
</script>
<style lang="scss" scoped>
$uni-font-size-base:14px;
$uni-text-color:#333;
$uni-font-size-sm:12px;
$uni-color-error: #e43d33;
$uni-opacity-disabled: 0.3;
$uni-text-color-disable:#c0c0c0;
$uni-primary: #2979ff !default;
.uni-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
}
.uni-calendar-item__weeks-box-text {
font-size: $uni-font-size-base;
color: $uni-text-color;
}
.uni-calendar-item__weeks-lunar-text {
font-size: $uni-font-size-sm;
color: $uni-text-color;
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 100rpx;
height: 100rpx;
}
.uni-calendar-item__weeks-box-circle {
position: absolute;
top: 5px;
right: 5px;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: $uni-color-error;
}
.uni-calendar-item--disable {
background-color: rgba(249, 249, 249, $uni-opacity-disabled);
color: $uni-text-color-disable;
}
.uni-calendar-item--isDay-text {
color: $uni-primary;
}
.uni-calendar-item--isDay {
background-color: $uni-primary;
opacity: 0.8;
color: #fff;
}
.uni-calendar-item--extra {
color: $uni-color-error;
opacity: 0.8;
}
.uni-calendar-item--checked {
background-color: $uni-primary;
color: #fff;
opacity: 0.8;
}
.uni-calendar-item--multiple {
background-color: $uni-primary;
color: #fff;
opacity: 0.8;
}
.uni-calendar-item--before-checked {
background-color: #ff5a5f;
color: #fff;
}
.uni-calendar-item--after-checked {
background-color: #ff5a5f;
color: #fff;
}
</style>

View File

@ -1,567 +0,0 @@
<template>
<view class="uni-calendar">
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
<view class="uni-calendar__header-btn-box" @click="close">
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text>
</view>
<view class="uni-calendar__header-btn-box" @click="confirm">
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text>
</view>
</view>
<view class="uni-calendar__header">
<view class="uni-calendar__header-btn-box" @click.stop="pre">
<view class="uni-calendar__header-btn uni-calendar--left"></view>
</view>
<picker mode="date" :value="date" fields="month" @change="bindDateChange">
<text class="uni-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text>
</picker>
<view class="uni-calendar__header-btn-box" @click.stop="next">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<text class="uni-calendar__backtoday" @click="backToday">{{todayText}}</text>
</view>
<view class="uni-calendar__box">
<view v-if="showMonth" class="uni-calendar__box-bg">
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
</view>
<view class="uni-calendar__weeks">
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{monText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
</view>
</view>
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import Calendar from './util.js';
import CalendarItem from './uni-calendar-item.vue'
import { initVueI18n } from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const { t } = initVueI18n(i18nMessages)
/**
* Calendar 日历
* @description 日历组件可以查看日期选择任意范围内的日期打点操作常用场景如酒店日期预订火车机票选择购买日期上下班打卡等
* @tutorial https://ext.dcloud.net.cn/plugin?id=56
* @property {String} date 自定义当前时间默认为今天
* @property {Boolean} lunar 显示农历
* @property {String} startDate 日期选择范围-开始日期
* @property {String} endDate 日期选择范围-结束日期
* @property {Boolean} range 范围选择
* @property {Boolean} insert = [true|false] 插入模式,默认为false
* @value true 弹窗模式
* @value false 插入模式
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
* @property {Array} selected 打点期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
* @property {Boolean} showMonth 是否选择月份为背景
* @event {Function} change 日期改变`insert :ture` 时生效
* @event {Function} confirm 确认选择`insert :false` 时生效
* @event {Function} monthSwitch 切换月份时触发
* @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
*/
export default {
components: {
CalendarItem
},
emits:['close','confirm','change','monthSwitch'],
props: {
date: {
type: String,
default: ''
},
selected: {
type: Array,
default () {
return []
}
},
lunar: {
type: Boolean,
default: false
},
startDate: {
type: String,
default: ''
},
endDate: {
type: String,
default: ''
},
range: {
type: Boolean,
default: false
},
insert: {
type: Boolean,
default: true
},
showMonth: {
type: Boolean,
default: true
},
clearDate: {
type: Boolean,
default: true
}
},
data() {
return {
show: false,
weeks: [],
calendar: {},
nowDate: '',
aniMaskShow: false
}
},
computed:{
/**
* for i18n
*/
okText() {
return t("uni-calender.ok")
},
cancelText() {
return t("uni-calender.cancel")
},
todayText() {
return t("uni-calender.today")
},
monText() {
return t("uni-calender.MON")
},
TUEText() {
return t("uni-calender.TUE")
},
WEDText() {
return t("uni-calender.WED")
},
THUText() {
return t("uni-calender.THU")
},
FRIText() {
return t("uni-calender.FRI")
},
SATText() {
return t("uni-calender.SAT")
},
SUNText() {
return t("uni-calender.SUN")
},
},
watch: {
date(newVal) {
// this.cale.setDate(newVal)
this.init(newVal)
},
startDate(val){
this.cale.resetSatrtDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
endDate(val){
this.cale.resetEndDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
selected(newVal) {
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
}
},
created() {
this.cale = new Calendar({
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
})
this.init(this.date)
},
methods: {
// 穿
clean() {},
bindDateChange(e) {
const value = e.detail.value + '-1'
this.setDate(value)
const { year,month } = this.cale.getDate(value)
this.$emit('monthSwitch', {
year,
month
})
},
/**
* 初始化日期显示
* @param {Object} date
*/
init(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(date)
},
/**
* 打开日历弹窗
*/
open() {
//
if (this.clearDate && !this.insert) {
this.cale.cleanMultipleStatus()
// this.cale.setDate(this.date)
this.init(this.date)
}
this.show = true
this.$nextTick(() => {
setTimeout(() => {
this.aniMaskShow = true
}, 50)
})
},
/**
* 关闭日历弹窗
*/
close() {
this.aniMaskShow = false
this.$nextTick(() => {
setTimeout(() => {
this.show = false
this.$emit('close')
}, 300)
})
},
/**
* 确认按钮
*/
confirm() {
this.setEmit('confirm')
this.close()
},
/**
* 变化触发
*/
change() {
if (!this.insert) return
this.setEmit('change')
},
/**
* 选择月份触发
*/
monthSwitch() {
let {
year,
month
} = this.nowDate
this.$emit('monthSwitch', {
year,
month: Number(month)
})
},
/**
* 派发事件
* @param {Object} name
*/
setEmit(name) {
let {
year,
month,
date,
fullDate,
lunar,
extraInfo
} = this.calendar
this.$emit(name, {
range: this.cale.multipleStatus,
year,
month,
date,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {}
})
},
/**
* 选择天触发
* @param {Object} weeks
*/
choiceDate(weeks) {
if (weeks.disable) return
this.calendar = weeks
//
this.cale.setMultiple(this.calendar.fullDate)
this.weeks = this.cale.weeks
this.change()
},
/**
* 回到今天
*/
backToday() {
const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
const date = this.cale.getDate(new Date())
const todayYearMonth = `${date.year}-${date.month}`
this.init(date.fullDate)
if(nowYearMonth !== todayYearMonth) {
this.monthSwitch()
}
this.change()
},
/**
* 上个月
*/
pre() {
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
this.setDate(preDate)
this.monthSwitch()
},
/**
* 下个月
*/
next() {
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
this.setDate(nextDate)
this.monthSwitch()
},
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.cale.getInfo(date)
}
}
}
</script>
<style lang="scss" scoped>
$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
$uni-border-color: #EDEDED;
$uni-text-color: #333;
$uni-bg-color-hover:#f1f1f1;
$uni-font-size-base:14px;
$uni-text-color-placeholder: #808080;
$uni-color-subtitle: #555555;
$uni-text-color-grey:#999;
.uni-calendar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-calendar__mask {
position: fixed;
bottom: 0;
top: 0;
left: 0;
right: 0;
background-color: $uni-bg-color-mask;
transition-property: opacity;
transition-duration: 0.3s;
opacity: 0;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--mask-show {
opacity: 1
}
.uni-calendar--fixed {
position: fixed;
/* #ifdef APP-NVUE */
bottom: 0;
/* #endif */
left: 0;
right: 0;
transition-property: transform;
transition-duration: 0.3s;
transform: translateY(460px);
/* #ifndef APP-NVUE */
bottom: calc(var(--window-bottom));
z-index: 99;
/* #endif */
}
.uni-calendar--ani-show {
transform: translateY(0);
}
.uni-calendar__content {
background-color: #fff;
}
.uni-calendar__header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
height: 50px;
border-bottom-color: $uni-border-color;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar--fixed-top {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 1px;
}
.uni-calendar--fixed-width {
width: 50px;
}
.uni-calendar__backtoday {
position: absolute;
right: 0;
top: 25rpx;
padding: 0 5px;
padding-left: 10px;
height: 25px;
line-height: 25px;
font-size: 12px;
border-top-left-radius: 25px;
border-bottom-left-radius: 25px;
color: $uni-text-color;
background-color: $uni-bg-color-hover;
}
.uni-calendar__header-text {
text-align: center;
width: 100px;
font-size: $uni-font-size-base;
color: $uni-text-color;
}
.uni-calendar__header-btn-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
}
.uni-calendar__header-btn {
width: 10px;
height: 10px;
border-left-color: $uni-text-color-placeholder;
border-left-style: solid;
border-left-width: 2px;
border-top-color: $uni-color-subtitle;
border-top-style: solid;
border-top-width: 2px;
}
.uni-calendar--left {
transform: rotate(-45deg);
}
.uni-calendar--right {
transform: rotate(135deg);
}
.uni-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-calendar__weeks-item {
flex: 1;
}
.uni-calendar__weeks-day {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
height: 45px;
border-bottom-color: #F5F5F5;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar__weeks-day-text {
font-size: 14px;
}
.uni-calendar__box {
position: relative;
}
.uni-calendar__box-bg {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.uni-calendar__box-bg-text {
font-size: 200px;
font-weight: bold;
color: $uni-text-color-grey;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
</style>

View File

@ -1,360 +0,0 @@
import CALENDAR from './calendar.js'
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
range
} = {}) {
// 当前日期
this.date = this.getDate(new Date()) // 当前初入日期
// 打点信息
this.selected = selected || [];
// 范围开始
this.startDate = startDate
// 范围结束
this.endDate = endDate
this.range = range
// 多选状态
this.cleanMultipleStatus()
// 每周日期
this.weeks = {}
// this._getWeek(this.date.fullDate)
}
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.selectDate = this.getDate(date)
this._getWeek(this.selectDate.fullDate)
}
/**
* 清理多选状态
*/
cleanMultipleStatus() {
this.multipleStatus = {
before: '',
after: '',
data: []
}
}
/**
* 重置开始日期
*/
resetSatrtDate(startDate) {
// 范围开始
this.startDate = startDate
}
/**
* 重置结束日期
*/
resetEndDate(endDate) {
// 范围结束
this.endDate = endDate
}
/**
* 获取任意时间
*/
getDate(date, AddDayCount = 0, str = 'day') {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break
case 'month':
if (dd.getDate() === 31 && AddDayCount>0) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
const preMonth = dd.getMonth()
dd.setMonth(preMonth + AddDayCount) // 获取AddDayCount天后的日期
const nextMonth = dd.getMonth()
// 处理 pre 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
if(AddDayCount<0 && preMonth!==0 && nextMonth-preMonth>AddDayCount){
dd.setMonth(nextMonth+(nextMonth-preMonth+AddDayCount))
}
// 处理 next 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
if(AddDayCount>0 && nextMonth-preMonth>AddDayCount){
dd.setMonth(nextMonth-(nextMonth-preMonth-AddDayCount))
}
}
break
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号不足10补0
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
}
/**
* 获取上月剩余天数
*/
_getLastMonthDays(firstDay, full) {
let dateArr = []
for (let i = firstDay; i > 0; i--) {
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
dateArr.push({
date: beforeDate,
month: full.month - 1,
lunar: this.getlunar(full.year, full.month - 1, beforeDate),
disable: true
})
}
return dateArr
}
/**
* 获取本月天数
*/
_currentMonthDys(dateData, full) {
let dateArr = []
let fullDate = this.date.fullDate
for (let i = 1; i <= dateData; i++) {
let nowDate = full.year + '-' + (full.month < 10 ?
full.month : full.month) + '-' + (i < 10 ?
'0' + i : i)
// 是否今天
let isDay = fullDate === nowDate
// 获取打点信息
let info = this.selected && this.selected.find((item) => {
if (this.dateEqual(nowDate, item.date)) {
return item
}
})
// 日期禁用
let disableBefore = true
let disableAfter = true
if (this.startDate) {
// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
disableBefore = this.dateCompare(this.startDate, nowDate)
}
if (this.endDate) {
// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
disableAfter = this.dateCompare(nowDate, this.endDate)
}
let multiples = this.multipleStatus.data
let checked = false
let multiplesStatus = -1
if (this.range) {
if (multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, nowDate)
})
}
if (multiplesStatus !== -1) {
checked = true
}
}
let data = {
fullDate: nowDate,
year: full.year,
date: i,
multiple: this.range ? checked : false,
beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate),
afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate),
month: full.month,
lunar: this.getlunar(full.year, full.month, i),
disable: !(disableBefore && disableAfter),
isDay
}
if (info) {
data.extraInfo = info
}
dateArr.push(data)
}
return dateArr
}
/**
* 获取下月天数
*/
_getNextMonthDays(surplus, full) {
let dateArr = []
for (let i = 1; i < surplus + 1; i++) {
dateArr.push({
date: i,
month: Number(full.month) + 1,
lunar: this.getlunar(full.year, Number(full.month) + 1, i),
disable: true
})
}
return dateArr
}
/**
* 获取当前日期详情
* @param {Object} date
*/
getInfo(date) {
if (!date) {
date = new Date()
}
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
return dateInfo
}
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
}
/**
* 比较时间是否相等
*/
dateEqual(before, after) {
// 计算截止时间
before = new Date(before.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
after = new Date(after.replace('-', '/').replace('-', '/'))
if (before.getTime() - after.getTime() === 0) {
return true
} else {
return false
}
}
/**
* 获取日期范围内所有日期
* @param {Object} begin
* @param {Object} end
*/
geDateAll(begin, end) {
var arr = []
var ab = begin.split('-')
var ae = end.split('-')
var db = new Date()
db.setFullYear(ab[0], ab[1] - 1, ab[2])
var de = new Date()
de.setFullYear(ae[0], ae[1] - 1, ae[2])
var unixDb = db.getTime() - 24 * 60 * 60 * 1000
var unixDe = de.getTime() - 24 * 60 * 60 * 1000
for (var k = unixDb; k <= unixDe;) {
k = k + 24 * 60 * 60 * 1000
arr.push(this.getDate(new Date(parseInt(k))).fullDate)
}
return arr
}
/**
* 计算阴历日期显示
*/
getlunar(year, month, date) {
return CALENDAR.solar2lunar(year, month, date)
}
/**
* 设置打点
*/
setSelectInfo(data, value) {
this.selected = value
this._getWeek(data)
}
/**
* 获取多选状态
*/
setMultiple(fullDate) {
let {
before,
after
} = this.multipleStatus
if (!this.range) return
if (before && after) {
this.multipleStatus.before = fullDate
this.multipleStatus.after = ''
this.multipleStatus.data = []
} else {
if (!before) {
this.multipleStatus.before = fullDate
} else {
this.multipleStatus.after = fullDate
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
} else {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
}
}
}
this._getWeek(fullDate)
}
/**
* 获取每周数据
* @param {Object} dateData
*/
_getWeek(dateData) {
const {
year,
month
} = this.getDate(dateData)
let firstDay = new Date(year, month - 1, 1).getDay()
let currentDay = new Date(year, month, 0).getDate()
let dates = {
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
nextMonthDays: [], // 下个月开始几天
weeks: []
}
let canlender = []
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
let weeks = {}
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
weeks[parseInt(i / 7)] = new Array(7)
}
weeks[parseInt(i / 7)][i % 7] = canlender[i]
}
this.canlender = canlender
this.weeks = weeks
}
//静态方法
// static init(date) {
// if (!this.instance) {
// this.instance = new Calendar(date);
// }
// return this.instance;
// }
}
export default Calendar

View File

@ -1,86 +0,0 @@
{
"id": "uni-calendar",
"displayName": "uni-calendar 日历",
"version": "1.4.12",
"description": "日历组件",
"keywords": [
"uni-ui",
"uniui",
"日历",
"",
"打卡",
"日历选择"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"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"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,103 +0,0 @@
## Calendar 日历
> **组件名uni-calendar**
> 代码块: `uCalendar`
日历组件
> **注意事项**
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
> - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js)
> - 仅支持自定义组件模式
> - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date()
> - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意
> - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动
### 安装方式
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
### 基本用法
在 ``template`` 中使用组件
```html
<view>
<uni-calendar
:insert="true"
:lunar="true"
:start-date="'2019-3-2'"
:end-date="'2019-5-20'"
@change="change"
/>
</view>
```
### 通过方法打开日历
需要设置 `insert``false`
```html
<view>
<uni-calendar
ref="calendar"
:insert="false"
@confirm="confirm"
/>
<button @click="open">打开日历</button>
</view>
```
```javascript
export default {
data() {
return {};
},
methods: {
open(){
this.$refs.calendar.open();
},
confirm(e) {
console.log(e);
}
}
};
```
## API
### Calendar Props
| 属性名 | 类型 | 默认值| 说明 |
| - | - | - | - |
| date | String |- | 自定义当前时间,默认为今天 |
| lunar | Boolean | false | 显示农历 |
| startDate | String |- | 日期选择范围-开始日期 |
| endDate | String |- | 日期选择范围-结束日期 |
| range | Boolean | false | 范围选择 |
| insert | Boolean | false | 插入模式,可选值ture插入模式false弹窗模式默认为插入模式 |
|clearDate |Boolean |true |弹窗模式是否清空上次选择内容 |
| selected | Array |- | 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
|showMonth | Boolean | true | 是否显示月份为背景 |
### Calendar Events
| 事件名 | 说明 |返回值|
| - | - | - |
| open | 弹出日历组件,`insert :false` 时生效|- |
## 组件示例
点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar)

View File

@ -1,38 +0,0 @@
## 1.4.42024-03-20
- 修复 titleBorder类型修正
## 1.4.32022-01-25
- 修复 初始化的时候 open 属性失效的bug
## 1.4.22022-01-21
- 修复 微信小程序resize后组件收起的bug
## 1.4.12021-11-22
- 修复 vue3中个别scss变量无法找到的问题
## 1.4.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-collapse](https://uniapp.dcloud.io/component/uniui/uni-collapse)
## 1.3.32021-08-17
- 优化 show-arrow 属性默认为true
## 1.3.22021-08-17
- 新增 show-arrow 属性,控制是否显示右侧箭头
## 1.3.12021-07-30
- 优化 vue3下小程序事件警告的问题
## 1.3.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.2.22021-07-21
- 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug
## 1.2.12021-07-21
- 优化 组件示例
## 1.2.02021-07-21
- 新增 组件折叠动画
- 新增 value\v-model 属性 ,动态修改面板折叠状态
- 新增 title 插槽 ,可定义面板标题
- 新增 border 属性 ,显示隐藏面板内容分隔线
- 新增 title-border 属性 ,显示隐藏面板标题分隔线
- 修复 resize 方法失效的Bug
- 修复 change 事件返回参数不正确的Bug
- 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法
## 1.1.72021-05-12
- 新增 组件示例地址
## 1.1.62021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.1.52021-02-05
- 调整为uni_modules目录规范

View File

@ -1,402 +0,0 @@
<template>
<view class="uni-collapse-item">
<!-- onClick(!isOpen) -->
<view @click="onClick(!isOpen)" class="uni-collapse-item__title"
:class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}">
<view class="uni-collapse-item__title-wrap">
<slot name="title">
<view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}">
<image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" />
<text class="uni-collapse-item__title-text">{{ title }}</text>
</view>
</slot>
</view>
<view v-if="showArrow"
:class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }"
class="uni-collapse-item__title-arrow">
<uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="bottom" />
</view>
</view>
<view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}"
:style="{height: (isOpen?height:0) +'px'}">
<view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content"
:class="{open:isheight,'uni-collapse-item--border':border&&isOpen}">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
/**
* CollapseItem 折叠面板子组件
* @description 折叠面板子组件
* @property {String} title 标题文字
* @property {String} thumb 标题左侧缩略图
* @property {String} name 唯一标志符
* @property {Boolean} open = [true|false] 是否展开组件
* @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线
* @property {String} border = ['auto'|'show'|'none'] 是否显示分隔线
* @property {Boolean} disabled = [true|false] 是否展开面板
* @property {Boolean} showAnimation = [true|false] 开启动画
* @property {Boolean} showArrow = [true|false] 是否显示右侧箭头
*/
export default {
name: 'uniCollapseItem',
props: {
//
title: {
type: String,
default: ''
},
name: {
type: [Number, String],
default: ''
},
//
disabled: {
type: Boolean,
default: false
},
// #ifdef APP-PLUS
// ,app
showAnimation: {
type: Boolean,
default: false
},
// #endif
// #ifndef APP-PLUS
//
showAnimation: {
type: Boolean,
default: true
},
// #endif
//
open: {
type: Boolean,
default: false
},
//
thumb: {
type: String,
default: ''
},
// 线
titleBorder: {
type: String,
default: 'auto'
},
border: {
type: Boolean,
default: true
},
showArrow: {
type: Boolean,
default: true
}
},
data() {
// TODO IDbug
const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
return {
isOpen: false,
isheight: null,
height: 0,
elId,
nameSync: 0
}
},
watch: {
open(val) {
this.isOpen = val
this.onClick(val, 'init')
}
},
updated(e) {
this.$nextTick(() => {
this.init(true)
})
},
created() {
this.collapse = this.getCollapse()
this.oldHeight = 0
this.onClick(this.open, 'init')
},
// #ifndef VUE3
// TODO vue2
destroyed() {
if (this.__isUnmounted) return
this.uninstall()
},
// #endif
// #ifdef VUE3
// TODO vue3
unmounted() {
this.__isUnmounted = true
this.uninstall()
},
// #endif
mounted() {
if (!this.collapse) return
if (this.name !== '') {
this.nameSync = this.name
} else {
this.nameSync = this.collapse.childrens.length + ''
}
if (this.collapse.names.indexOf(this.nameSync) === -1) {
this.collapse.names.push(this.nameSync)
} else {
console.warn(`name 值 ${this.nameSync} 重复`);
}
if (this.collapse.childrens.indexOf(this) === -1) {
this.collapse.childrens.push(this)
}
this.init()
},
methods: {
init(type) {
// #ifndef APP-NVUE
this.getCollapseHeight(type)
// #endif
// #ifdef APP-NVUE
this.getNvueHwight(type)
// #endif
},
uninstall() {
if (this.collapse) {
this.collapse.childrens.forEach((item, index) => {
if (item === this) {
this.collapse.childrens.splice(index, 1)
}
})
this.collapse.names.forEach((item, index) => {
if (item === this.nameSync) {
this.collapse.names.splice(index, 1)
}
})
}
},
onClick(isOpen, type) {
if (this.disabled) return
this.isOpen = isOpen
if (this.isOpen && this.collapse) {
this.collapse.setAccordion(this)
}
if (type !== 'init') {
this.collapse.onChange(isOpen, this)
}
},
getCollapseHeight(type, index = 0) {
const views = uni.createSelectorQuery().in(this)
views
.select(`#${this.elId}`)
.fields({
size: true
}, data => {
// TODO
if (index >= 10) return
if (!data) {
index++
this.getCollapseHeight(false, index)
return
}
// #ifdef APP-NVUE
this.height = data.height + 1
// #endif
// #ifndef APP-NVUE
this.height = data.height
// #endif
this.isheight = true
if (type) return
this.onClick(this.isOpen, 'init')
})
.exec()
},
getNvueHwight(type) {
const result = dom.getComponentRect(this.$refs['collapse--hook'], option => {
if (option && option.result && option.size) {
// #ifdef APP-NVUE
this.height = option.size.height + 1
// #endif
// #ifndef APP-NVUE
this.height = option.size.height
// #endif
this.isheight = true
if (type) return
this.onClick(this.open, 'init')
}
})
},
/**
* 获取父元素实例
*/
getCollapse(name = 'uniCollapse') {
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;
}
}
}
</script>
<style lang="scss">
.uni-collapse-item {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
&__title {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
transition: border-bottom-color .3s;
// transition-property: border-bottom-color;
// transition-duration: 5s;
&-wrap {
width: 100%;
flex: 1;
}
&-box {
padding: 0 15px;
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 48px;
line-height: 48px;
background-color: #fff;
color: #303133;
font-size: 13px;
font-weight: 500;
/* #ifdef H5 */
cursor: pointer;
outline: none;
/* #endif */
&.is-disabled {
.uni-collapse-item__title-text {
color: #999;
}
}
}
&.uni-collapse-item-border {
border-bottom: 1px solid #ebeef5;
}
&.is-open {
border-bottom-color: transparent;
}
&-img {
height: 22px;
width: 22px;
margin-right: 10px;
}
&-text {
flex: 1;
font-size: 14px;
/* #ifndef APP-NVUE */
white-space: nowrap;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
overflow: hidden;
text-overflow: ellipsis;
}
&-arrow {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
margin-right: 10px;
transform: rotate(0deg);
&-active {
transform: rotate(-180deg);
}
}
}
&__wrap {
/* #ifndef APP-NVUE */
will-change: height;
box-sizing: border-box;
/* #endif */
background-color: #fff;
overflow: hidden;
position: relative;
height: 0;
&.is--transition {
// transition: all 0.3s;
transition-property: height, border-bottom-width;
transition-duration: 0.3s;
/* #ifndef APP-NVUE */
will-change: height;
/* #endif */
}
&-content {
position: absolute;
font-size: 13px;
color: #303133;
// transition: height 0.3s;
border-bottom-color: transparent;
border-bottom-style: solid;
border-bottom-width: 0;
&.uni-collapse-item--border {
border-bottom-width: 1px;
border-bottom-color: red;
border-bottom-color: #ebeef5;
}
&.open {
position: relative;
}
}
}
&--animation {
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
}
}
</style>

View File

@ -1,147 +0,0 @@
<template>
<view class="uni-collapse">
<slot />
</view>
</template>
<script>
/**
* Collapse 折叠面板
* @description 展示可以折叠 / 展开的内容区域
* @tutorial https://ext.dcloud.net.cn/plugin?id=23
* @property {String|Array} value 当前激活面板改变时触发(如果是手风琴模式参数类型为string否则为array)
* @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果
* @event {Function} change 切换面板时触发如果是手风琴模式返回类型为string否则为array
*/
export default {
name: 'uniCollapse',
emits:['change','activeItem','input','update:modelValue'],
props: {
value: {
type: [String, Array],
default: ''
},
modelValue: {
type: [String, Array],
default: ''
},
accordion: {
//
type: [Boolean, String],
default: false
},
},
data() {
return {}
},
computed: {
// TODO vue2 vue3
dataValue() {
let value = (typeof this.value === 'string' && this.value === '') ||
(Array.isArray(this.value) && this.value.length === 0)
let modelValue = (typeof this.modelValue === 'string' && this.modelValue === '') ||
(Array.isArray(this.modelValue) && this.modelValue.length === 0)
if (value) {
return this.modelValue
}
if (modelValue) {
return this.value
}
return this.value
}
},
watch: {
dataValue(val) {
this.setOpen(val)
}
},
created() {
this.childrens = []
this.names = []
},
mounted() {
this.$nextTick(()=>{
this.setOpen(this.dataValue)
})
},
methods: {
setOpen(val) {
let str = typeof val === 'string'
let arr = Array.isArray(val)
this.childrens.forEach((vm, index) => {
if (str) {
if (val === vm.nameSync) {
if (!this.accordion) {
console.warn('accordion 属性为 false ,v-model 类型应该为 array')
return
}
vm.isOpen = true
}
}
if (arr) {
val.forEach(v => {
if (v === vm.nameSync) {
if (this.accordion) {
console.warn('accordion 属性为 true ,v-model 类型应该为 string')
return
}
vm.isOpen = true
}
})
}
})
this.emit(val)
},
setAccordion(self) {
if (!this.accordion) return
this.childrens.forEach((vm, index) => {
if (self !== vm) {
vm.isOpen = false
}
})
},
resize() {
this.childrens.forEach((vm, index) => {
// #ifndef APP-NVUE
vm.getCollapseHeight()
// #endif
// #ifdef APP-NVUE
vm.getNvueHwight()
// #endif
})
},
onChange(isOpen, self) {
let activeItem = []
if (this.accordion) {
activeItem = isOpen ? self.nameSync : ''
} else {
this.childrens.forEach((vm, index) => {
if (vm.isOpen) {
activeItem.push(vm.nameSync)
}
})
}
this.$emit('change', activeItem)
this.emit(activeItem)
},
emit(val){
this.$emit('input', val)
this.$emit('update:modelValue', val)
}
}
}
</script>
<style lang="scss" >
.uni-collapse {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
flex-direction: column;
background-color: #fff;
}
</style>

View File

@ -1,86 +0,0 @@
{
"id": "uni-collapse",
"displayName": "uni-collapse 折叠面板",
"version": "1.4.4",
"description": "Collapse 组件,可以折叠 / 展开的内容区域。",
"keywords": [
"uni-ui",
"折叠",
"折叠面板",
"手风琴"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"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"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,12 +0,0 @@
## Collapse 折叠面板
> **组件名uni-collapse**
> 代码块: `uCollapse`
> 关联组件:`uni-collapse-item`、`uni-icons`。
折叠面板用来折叠/显示过长的内容或者是列表。通常是在多内容分类项使用,折叠不重要的内容,显示重要内容。点击可以展开折叠部分。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-collapse)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,17 +0,0 @@
## 1.0.22024-09-21
- 新增 clearAble属性
## 1.0.12021-11-23
- 优化 label、label-width 属性
## 1.0.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-combox](https://uniapp.dcloud.io/component/uniui/uni-combox)
## 0.1.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.0.62021-05-12
- 新增 组件示例地址
## 0.0.52021-04-21
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
## 0.0.42021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 0.0.32021-02-04
- 调整为uni_modules目录规范

View File

@ -1,284 +0,0 @@
<template>
<view class="uni-combox" :class="border ? '' : 'uni-combox__no-border'">
<view v-if="label" class="uni-combox__label" :style="labelStyle">
<text>{{label}}</text>
</view>
<view class="uni-combox__input-box">
<input class="uni-combox__input" type="text" :placeholder="placeholder" placeholder-class="uni-combox__input-plac"
v-model="inputVal" @input="onInput" @focus="onFocus" @blur="onBlur" />
<uni-icons v-if="!inputVal || !clearAble" :type="showSelector? 'top' : 'bottom'" size="14" color="#999" @click="toggleSelector">
</uni-icons>
<uni-icons v-if="inputVal && clearAble" type="clear" size="24" color="#999" @click="clean">
</uni-icons>
</view>
<view class="uni-combox__selector" v-if="showSelector">
<view class="uni-popper__arrow"></view>
<scroll-view scroll-y="true" class="uni-combox__selector-scroll">
<view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0">
<text>{{emptyTips}}</text>
</view>
<view class="uni-combox__selector-item" v-for="(item,index) in filterCandidates" :key="index"
@click="onSelectorClick(index)">
<text>{{item}}</text>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
/**
* Combox 组合输入框
* @description 组合输入框一般用于既可以输入也可以选择的场景
* @tutorial https://ext.dcloud.net.cn/plugin?id=1261
* @property {String} label 左侧文字
* @property {String} labelWidth 左侧内容宽度
* @property {String} placeholder 输入框占位符
* @property {Array} candidates 候选项列表
* @property {String} emptyTips 筛选结果为空时显示的文字
* @property {String} value 组合框的值
*/
export default {
name: 'uniCombox',
emits: ['input', 'update:modelValue'],
props: {
clearAble: {
type: Boolean,
default: false
},
border: {
type: Boolean,
default: true
},
label: {
type: String,
default: ''
},
labelWidth: {
type: String,
default: 'auto'
},
placeholder: {
type: String,
default: ''
},
candidates: {
type: Array,
default () {
return []
}
},
emptyTips: {
type: String,
default: '无匹配项'
},
// #ifndef VUE3
value: {
type: [String, Number],
default: ''
},
// #endif
// #ifdef VUE3
modelValue: {
type: [String, Number],
default: ''
},
// #endif
},
data() {
return {
showSelector: false,
inputVal: ''
}
},
computed: {
labelStyle() {
if (this.labelWidth === 'auto') {
return ""
}
return `width: ${this.labelWidth}`
},
filterCandidates() {
return this.candidates.filter((item) => {
return item.toString().indexOf(this.inputVal) > -1
})
},
filterCandidatesLength() {
return this.filterCandidates.length
}
},
watch: {
// #ifndef VUE3
value: {
handler(newVal) {
this.inputVal = newVal
},
immediate: true
},
// #endif
// #ifdef VUE3
modelValue: {
handler(newVal) {
this.inputVal = newVal
},
immediate: true
},
// #endif
},
methods: {
toggleSelector() {
this.showSelector = !this.showSelector
},
onFocus() {
this.showSelector = true
},
onBlur() {
setTimeout(() => {
this.showSelector = false
}, 153)
},
onSelectorClick(index) {
this.inputVal = this.filterCandidates[index]
this.showSelector = false
this.$emit('input', this.inputVal)
this.$emit('update:modelValue', this.inputVal)
},
onInput() {
setTimeout(() => {
this.$emit('input', this.inputVal)
this.$emit('update:modelValue', this.inputVal)
})
},
clean() {
this.inputVal = ''
this.onInput()
}
}
}
</script>
<style lang="scss" scoped>
.uni-combox {
font-size: 14px;
border: 1px solid #DCDFE6;
border-radius: 4px;
padding: 6px 10px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
// height: 40px;
flex-direction: row;
align-items: center;
// border-bottom: solid 1px #DDDDDD;
}
.uni-combox__label {
font-size: 16px;
line-height: 22px;
padding-right: 10px;
color: #999999;
}
.uni-combox__input-box {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
}
.uni-combox__input {
flex: 1;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.uni-combox__input-plac {
font-size: 14px;
color: #999;
}
.uni-combox__selector {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
position: absolute;
top: calc(100% + 12px);
left: 0;
width: 100%;
background-color: #FFFFFF;
border: 1px solid #EBEEF5;
border-radius: 6px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 2;
padding: 4px 0;
}
.uni-combox__selector-scroll {
/* #ifndef APP-NVUE */
max-height: 200px;
box-sizing: border-box;
/* #endif */
}
.uni-combox__selector-empty,
.uni-combox__selector-item {
/* #ifndef APP-NVUE */
display: flex;
cursor: pointer;
/* #endif */
line-height: 36px;
font-size: 14px;
text-align: center;
// border-bottom: solid 1px #DDDDDD;
padding: 0px 10px;
}
.uni-combox__selector-item:hover {
background-color: #f9f9f9;
}
.uni-combox__selector-empty:last-child,
.uni-combox__selector-item:last-child {
/* #ifndef APP-NVUE */
border-bottom: none;
/* #endif */
}
// picker
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-top-width: 0;
border-bottom-color: #fff;
}
.uni-combox__no-border {
border: none;
}
</style>

View File

@ -1,88 +0,0 @@
{
"id": "uni-combox",
"displayName": "uni-combox 组合框",
"version": "1.0.2",
"description": "可以选择也可以输入的表单项 ",
"keywords": [
"uni-ui",
"uniui",
"combox",
"组合框",
"select"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"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"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,11 +0,0 @@
## Combox 组合框
> **组件名uni-combox**
> 代码块: `uCombox`
组合框组件。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-combox)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,28 +0,0 @@
## 1.2.42024-09-21
- 新增 支持控制显示位数 默认显示2位
## 1.2.32024-02-20
- 新增 支持控制小时分钟的显隐showHour showMinute
## 1.2.22022-01-19
- 修复 在微信小程序中样式不生效的bug
## 1.2.12022-01-18
- 新增 update 方法 ,在动态更新时间后,刷新组件
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-countdown](https://uniapp.dcloud.io/component/uniui/uni-countdown)
## 1.1.32021-10-18
- 重构
- 新增 font-size 支持自定义字体大小
## 1.1.22021-08-24
- 新增 支持国际化
## 1.1.12021-07-30
- 优化 vue3下小程序事件警告的问题
## 1.1.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.52021-06-18
- 修复 uni-countdown 重复赋值跳两秒的 bug
## 1.0.42021-05-12
- 新增 组件示例地址
## 1.0.32021-05-08
- 修复 uni-countdown 不能控制倒计时的 bug
## 1.0.22021-02-04
- 调整为uni_modules目录规范

View File

@ -1,6 +0,0 @@
{
"uni-countdown.day": "day",
"uni-countdown.h": "h",
"uni-countdown.m": "m",
"uni-countdown.s": "s"
}

View File

@ -1,8 +0,0 @@
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
}

View File

@ -1,6 +0,0 @@
{
"uni-countdown.day": "天",
"uni-countdown.h": "时",
"uni-countdown.m": "分",
"uni-countdown.s": "秒"
}

View File

@ -1,6 +0,0 @@
{
"uni-countdown.day": "天",
"uni-countdown.h": "時",
"uni-countdown.m": "分",
"uni-countdown.s": "秒"
}

View File

@ -1,276 +0,0 @@
<template>
<view class="uni-countdown">
<text v-if="showDay" :style="[timeStyle]" class="uni-countdown__number">{{ d }}</text>
<text v-if="showDay" :style="[splitorStyle]" class="uni-countdown__splitor">{{dayText}}</text>
<text v-if="showHour" :style="[timeStyle]" class="uni-countdown__number">{{ h }}</text>
<text v-if="showHour" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : hourText }}</text>
<text v-if="showMinute" :style="[timeStyle]" class="uni-countdown__number">{{ i }}</text>
<text v-if="showMinute" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : minuteText }}</text>
<text :style="[timeStyle]" class="uni-countdown__number">{{ s }}</text>
<text v-if="!showColon" :style="[splitorStyle]" class="uni-countdown__splitor">{{secondText}}</text>
</view>
</template>
<script>
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const {
t
} = initVueI18n(messages)
/**
* Countdown 倒计时
* @description 倒计时组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=25
* @property {String} backgroundColor 背景色
* @property {String} color 文字颜色
* @property {Number} day 天数
* @property {Number} hour 小时
* @property {Number} minute 分钟
* @property {Number} second
* @property {Number} timestamp 时间戳
* @property {Boolean} showDay = [true|false] 是否显示天数
* @property {Boolean} showHour = [true|false] 是否显示小时
* @property {Boolean} showMinute = [true|false] 是否显示分钟
* @property {Boolean} show-colon = [true|false] 是否以冒号为分隔符
* @property {String} splitorColor 分割符号颜色
* @event {Function} timeup 倒计时时间到触发事件
* @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown>
*/
export default {
name: 'UniCountdown',
emits: ['timeup'],
props: {
showDay: {
type: Boolean,
default: true
},
showHour: {
type: Boolean,
default: true
},
showMinute: {
type: Boolean,
default: true
},
showColon: {
type: Boolean,
default: true
},
start: {
type: Boolean,
default: true
},
backgroundColor: {
type: String,
default: ''
},
color: {
type: String,
default: '#333'
},
fontSize: {
type: Number,
default: 14
},
splitorColor: {
type: String,
default: '#333'
},
day: {
type: Number,
default: 0
},
hour: {
type: Number,
default: 0
},
minute: {
type: Number,
default: 0
},
second: {
type: Number,
default: 0
},
timestamp: {
type: Number,
default: 0
},
filterShow : {
type:Object,
default:{}
}
},
data() {
return {
timer: null,
syncFlag: false,
d: '00',
h: '00',
i: '00',
s: '00',
leftTime: 0,
seconds: 0
}
},
computed: {
dayText() {
return t("uni-countdown.day")
},
hourText(val) {
return t("uni-countdown.h")
},
minuteText(val) {
return t("uni-countdown.m")
},
secondText(val) {
return t("uni-countdown.s")
},
timeStyle() {
const {
color,
backgroundColor,
fontSize
} = this
return {
color,
backgroundColor,
fontSize: `${fontSize}px`,
width: `${fontSize * 22 / 14}px`, // 14px
lineHeight: `${fontSize * 20 / 14}px`,
borderRadius: `${fontSize * 3 / 14}px`,
}
},
splitorStyle() {
const { splitorColor, fontSize, backgroundColor } = this
return {
color: splitorColor,
fontSize: `${fontSize * 12 / 14}px`,
margin: backgroundColor ? `${fontSize * 4 / 14}px` : ''
}
}
},
watch: {
day(val) {
this.changeFlag()
},
hour(val) {
this.changeFlag()
},
minute(val) {
this.changeFlag()
},
second(val) {
this.changeFlag()
},
start: {
immediate: true,
handler(newVal, oldVal) {
if (newVal) {
this.startData();
} else {
if (!oldVal) return
clearInterval(this.timer)
}
}
}
},
created: function(e) {
this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
this.countDown()
},
// #ifndef VUE3
destroyed() {
clearInterval(this.timer)
},
// #endif
// #ifdef VUE3
unmounted() {
clearInterval(this.timer)
},
// #endif
methods: {
toSeconds(timestamp, day, hours, minutes, seconds) {
if (timestamp) {
return timestamp - parseInt(new Date().getTime() / 1000, 10)
}
return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds
},
timeUp() {
clearInterval(this.timer)
this.$emit('timeup')
},
countDown() {
let seconds = this.seconds
let [day, hour, minute, second] = [0, 0, 0, 0]
if (seconds > 0) {
day = Math.floor(seconds / (60 * 60 * 24))
hour = Math.floor(seconds / (60 * 60)) - (day * 24)
minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
} else {
this.timeUp()
}
this.d = String(day).padStart(this.validFilterShow(this.filterShow.d), '0')
this.h = String(hour).padStart(this.validFilterShow(this.filterShow.h), '0')
this.i = String(minute).padStart(this.validFilterShow(this.filterShow.m), '0')
this.s = String(second).padStart(this.validFilterShow(this.filterShow.s), '0')
},
validFilterShow(filter){
return (filter && filter > 0) ? filter : 2;
},
startData() {
this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
if (this.seconds <= 0) {
this.seconds = this.toSeconds(0, 0, 0, 0, 0)
this.countDown()
return
}
clearInterval(this.timer)
this.countDown()
this.timer = setInterval(() => {
this.seconds--
if (this.seconds < 0) {
this.timeUp()
return
}
this.countDown()
}, 1000)
},
update(){
this.startData();
},
changeFlag() {
if (!this.syncFlag) {
this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
this.startData();
this.syncFlag = true;
}
}
}
}
</script>
<style lang="scss" scoped>
$font-size: 14px;
.uni-countdown {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
&__splitor {
margin: 0 2px;
font-size: $font-size;
color: #333;
}
&__number {
border-radius: 3px;
text-align: center;
font-size: $font-size;
}
}
</style>

View File

@ -1,84 +0,0 @@
{
"id": "uni-countdown",
"displayName": "uni-countdown 倒计时",
"version": "1.2.4",
"description": "CountDown 倒计时组件",
"keywords": [
"uni-ui",
"uniui",
"countdown",
"倒计时"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"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"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,10 +0,0 @@
## CountDown 倒计时
> **组件名uni-countdown**
> 代码块: `uCountDown`
倒计时组件。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-countdown)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,79 +0,0 @@
## 2.0.12024-08-22
- 修复 uni-app-x v-model 没有更新传入值的 bug
## 2.0.02023-12-11
- 新增 支持 uni-app-x
## 1.1.22023-04-11
- 修复 更改 modelValue 报错的 bug
- 修复 v-for 未使用 key 值控制台 warning
## 1.1.12023-02-21
- 修复代码合并时引发 value 属性为空时不渲染数据的问题
## 1.1.02023-02-15
- 修复 localdata 不支持动态更新的bug
## 1.0.92023-02-15
- 修复 localdata 不支持动态更新的bug
## 1.0.82022-09-16
- 可以使用 uni-scss 控制主题色
## 1.0.72022-07-06
- 优化 pc端图标位置不正确的问题
## 1.0.62022-07-05
- 优化 显示样式
## 1.0.52022-07-04
- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug
## 1.0.42022-04-19
- 修复 字节小程序 本地数据无法选择下一级的Bug
## 1.0.32022-02-25
- 修复 nvue 不支持的 v-show 的 bug
## 1.0.22022-02-25
- 修复 条件编译 nvue 不支持的 css 样式
## 1.0.12021-11-23
- 修复 由上个版本引发的map、v-model等属性不生效的bug
## 1.0.02021-11-19
- 优化 组件 UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
## 0.4.92021-10-28
- 修复 VUE2 v-model 概率无效的 bug
## 0.4.82021-10-27
- 修复 v-model 概率无效的 bug
## 0.4.72021-10-25
- 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+
- 修复 树型 uniCloud 数据类型为 int 时报错的 bug
## 0.4.62021-10-19
- 修复 非 VUE3 v-model 为 0 时无法选中的 bug
## 0.4.52021-09-26
- 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效
- 修复 readonly 为 true 时报错的 bug
## 0.4.42021-09-26
- 修复 上一版本造成的 map 属性失效的 bug
- 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略
## 0.4.32021-09-24
- 修复 某些情况下级联未触发的 bug
## 0.4.22021-09-23
- 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用
- 新增 选项内容过长自动添加省略号
## 0.4.12021-09-15
- 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段
## 0.4.02021-07-13
- 组件兼容 vue3如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.3.52021-06-04
- 修复 无法加载云端数据的问题
## 0.3.42021-05-28
- 修复 v-model 无效问题
- 修复 loaddata 为空数据组时加载时间过长问题
- 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点
## 0.3.32021-05-12
- 新增 组件示例地址
## 0.3.22021-04-22
- 修复 非树形数据有 where 属性查询报错的问题
## 0.3.12021-04-15
- 修复 本地数据概率无法回显时问题
## 0.3.02021-04-07
- 新增 支持云端非树形表结构数据
- 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题
## 0.2.02021-03-15
- 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题
## 0.1.92021-03-09
- 修复 微信小程序某些情况下无法选择的问题
## 0.1.82021-02-05
- 优化 部分样式在 nvue 上的兼容表现
## 0.1.72021-02-05
- 调整为 uni_modules 目录规范

View File

@ -1,45 +0,0 @@
// #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

View File

@ -1,381 +0,0 @@
<template>
<view class="uni-data-tree">
<view class="uni-data-tree-input" @click="handleInput">
<slot :data="selectedPaths" :error="error">
<view class="input-value" :class="{'input-value-border': border}">
<text v-if="error!=null" class="error-text">{{error!.errMsg}}</text>
<scroll-view v-if="selectedPaths.length" class="selected-path" scroll-x="true">
<view class="selected-list">
<template v-for="(item, index) in selectedPaths">
<text class="text-color">{{item[mappingTextName]}}</text>
<text v-if="index<selectedPaths.length-1" class="input-split-line">{{split}}</text>
</template>
</view>
</scroll-view>
<text v-else-if="error==null&&!loading" class="placeholder">{{placeholder}}</text>
<view v-if="!readonly" class="arrow-area">
<view class="input-arrow"></view>
</view>
</view>
</slot>
<view v-if="loading && !isOpened" class="selected-loading">
<slot name="picker-loading" :loading="loading"></slot>
</view>
</view>
<view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view>
<view class="uni-data-tree-dialog" v-if="isOpened">
<view class="uni-popper__arrow"></view>
<view class="dialog-caption">
<view class="dialog-title-view">
<text class="dialog-title">{{popupTitle}}</text>
</view>
<view class="dialog-close" @click="handleClose">
<view class="dialog-close-plus" data-id="close"></view>
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
</view>
</view>
<view ref="pickerView" class="uni-data-pickerview">
<view v-if="error!=null" class="error">
<text class="error-text">{{error!.errMsg}}</text>
</view>
<scroll-view v-if="!isCloudDataList" :scroll-x="true">
<view class="selected-node-list">
<template v-for="(item, index) in selectedNodes">
<text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}"
@click="onTabSelect(index)">
{{item[mappingTextName]}}
</text>
</template>
</view>
</scroll-view>
<list-view class="list-view" :scroll-y="true">
<list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)">
<text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text>
<text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text>
</list-item>
</list-view>
<view class="loading-cover" v-if="loading">
<slot name="pickerview-loading" :loading="loading"></slot>
</view>
</view>
</view>
</view>
</template>
<script>
import { dataPicker } from "../uni-data-pickerview/uni-data-picker.uts"
/**
* DataPicker 级联选择
* @description 支持单列、和多列级联选择。列数没有限制如果屏幕显示不全顶部tab区域会左右滚动。
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {String} popup-title 弹出窗口标题
* @property {Array} localdata 本地数据,参考
* @property {Boolean} border = [true|false] 是否有边框
* @property {Boolean} readonly = [true|false] 是否仅读
* @property {Boolean} preload = [true|false] 是否预加载数据
* @value true 开启预加载数据,点击弹出窗口后显示已加载数据
* @value false 关闭预加载数据,点击弹出窗口后开始加载数据
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询,仅查询当前选中节点
* @value false 关闭分布查询,一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
* @event {Function} popupshow 弹出的选择窗口打开时触发此事件
* @event {Function} popuphide 弹出的选择窗口关闭时触发此事件
*/
export default {
name: 'UniDataPicker',
emits: ['popupopened', 'popupclosed', 'nodeclick', 'change', 'input', 'update:modelValue', 'inputclick'],
mixins: [dataPicker],
props: {
popupTitle: {
type: String,
default: '请选择'
},
placeholder: {
type: String,
default: '请选择'
},
heightMobile: {
type: String,
default: ''
},
readonly: {
type: Boolean,
default: false
},
clearIcon: {
type: Boolean,
default: true
},
border: {
type: Boolean,
default: true
},
split: {
type: String,
default: '/'
},
ellipsis: {
type: Boolean,
default: true
}
},
data() {
return {
isOpened: false
}
},
computed: {
isShowClearIcon() : boolean {
if (this.readonly) {
return false
}
if (this.clearIcon && this.selectedPaths.length > 0) {
return true
}
return false
}
},
created() {
this.load()
},
methods: {
clear() {
},
load() {
if (this.isLocalData) {
this.loadLocalData()
} else if (this.isCloudDataList || this.isCloudDataTree) {
this.loadCloudDataPath()
}
},
show() {
this.isOpened = true
this.$emit('popupopened')
if (!this.hasCloudTreeData) {
this.loadData()
}
},
hide() {
this.isOpened = false
this.$emit('popupclosed')
},
handleInput() {
if (this.readonly) {
this.$emit('inputclick')
} else {
this.show()
}
},
handleClose() {
this.hide()
},
onFinish() {
this.selectedPaths = this.getChangeNodes()
this.$emit('update:modelValue', this.selectedPaths)
this.$emit('change', this.selectedPaths)
this.hide()
}
}
}
</script>
<style>
@import url("../uni-data-pickerview/uni-data-pickerview.css");
.uni-data-tree {
position: relative;
}
.uni-data-tree-input {
position: relative;
}
.selected-loading {
display: flex;
justify-content: center;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.error-text {
flex: 1;
font-size: 12px;
color: #DD524D;
}
.input-value {
flex-direction: row;
align-items: center;
flex-wrap: nowrap;
padding: 5px 5px;
padding-right: 5px;
overflow: hidden;
min-height: 28px;
}
.input-value-border {
border: 1px solid #e5e5e5;
border-radius: 5px;
}
.selected-path {
flex: 1;
flex-direction: row;
overflow: hidden;
}
.load-more {
width: 40px;
}
.selected-list {
flex-direction: row;
flex-wrap: nowrap;
}
.selected-item {
flex-direction: row;
flex-wrap: nowrap;
}
.text-color {
font-size: 14px;
color: #333;
}
.placeholder {
color: grey;
font-size: 14px;
}
.input-split-line {
opacity: .5;
margin-left: 1px;
margin-right: 1px;
}
.arrow-area {
position: relative;
padding: 0 12px;
margin-left: auto;
justify-content: center;
transform: rotate(-45deg);
transform-origin: center;
}
.input-arrow {
width: 8px;
height: 8px;
border-left: 2px solid #999;
border-bottom: 2px solid #999;
}
.uni-data-tree-cover {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .4);
flex-direction: column;
z-index: 100;
}
.uni-data-tree-dialog {
position: fixed;
left: 0;
top: 20%;
right: 0;
bottom: 0;
background-color: #FFFFFF;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
flex-direction: column;
z-index: 102;
overflow: hidden;
}
.dialog-caption {
position: relative;
flex-direction: row;
}
.dialog-title-view {
flex: 1;
}
.dialog-title {
align-self: center;
padding: 0 10px;
line-height: 44px;
}
.dialog-close {
position: absolute;
top: 0;
right: 0;
bottom: 0;
flex-direction: row;
align-items: center;
padding: 0 15px;
}
.dialog-close-plus {
width: 16px;
height: 2px;
background-color: #666;
border-radius: 2px;
transform: rotate(45deg);
}
.dialog-close-rotate {
position: absolute;
transform: rotate(-45deg);
}
.uni-data-pickerview {
flex: 1;
}
.icon-clear {
display: flex;
align-items: center;
}
/* #ifdef H5 */
@media all and (min-width: 768px) {
.uni-data-tree-cover {
background-color: transparent;
}
.uni-data-tree-dialog {
position: absolute;
top: 55px;
height: auto;
min-height: 400px;
max-height: 50vh;
background-color: #fff;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
overflow: unset;
}
.dialog-caption {
display: none;
}
}
/* #endif */
</style>

View File

@ -1,551 +0,0 @@
<template>
<view class="uni-data-tree">
<view class="uni-data-tree-input" @click="handleInput">
<slot :options="options" :data="inputSelected" :error="errorMessage">
<view class="input-value" :class="{'input-value-border': border}">
<text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text>
<view v-else-if="loading && !isOpened" class="selected-area">
<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
</view>
<scroll-view v-else-if="inputSelected.length" class="selected-area" scroll-x="true">
<view class="selected-list">
<view class="selected-item" v-for="(item,index) in inputSelected" :key="index">
<text class="text-color">{{item.text}}</text><text v-if="index<inputSelected.length-1"
class="input-split-line">{{split}}</text>
</view>
</view>
</scroll-view>
<text v-else class="selected-area placeholder">{{placeholder}}</text>
<view v-if="clearIcon && !readonly && inputSelected.length" class="icon-clear" @click.stop="clear">
<uni-icons type="clear" color="#c0c4cc" size="24"></uni-icons>
</view>
<view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly ">
<view class="input-arrow"></view>
</view>
</view>
</slot>
</view>
<view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view>
<view class="uni-data-tree-dialog" v-if="isOpened">
<view class="uni-popper__arrow"></view>
<view class="dialog-caption">
<view class="title-area">
<text class="dialog-title">{{popupTitle}}</text>
</view>
<view class="dialog-close" @click="handleClose">
<view class="dialog-close-plus" data-id="close"></view>
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
</view>
</view>
<data-picker-view class="picker-view" ref="pickerView" v-model="dataValue" :localdata="localdata"
:preload="preload" :collection="collection" :field="field" :orderby="orderby" :where="where"
:step-searh="stepSearh" :self-field="selfField" :parent-field="parentField" :managed-mode="true" :map="map"
:ellipsis="ellipsis" @change="onchange" @datachange="ondatachange" @nodeclick="onnodeclick">
</data-picker-view>
</view>
</view>
</template>
<script>
import dataPicker from "../uni-data-pickerview/uni-data-picker.js"
import DataPickerView from "../uni-data-pickerview/uni-data-pickerview.vue"
/**
* DataPicker 级联选择
* @description 支持单列和多列级联选择列数没有限制如果屏幕显示不全顶部tab区域会左右滚动
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {String} popup-title 弹出窗口标题
* @property {Array} localdata 本地数据参考
* @property {Boolean} border = [true|false] 是否有边框
* @property {Boolean} readonly = [true|false] 是否仅读
* @property {Boolean} preload = [true|false] 是否预加载数据
* @value true 开启预加载数据点击弹出窗口后显示已加载数据
* @value false 关闭预加载数据点击弹出窗口后开始加载数据
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询仅查询当前选中节点
* @value false 关闭分布查询一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
* @event {Function} popupshow 弹出的选择窗口打开时触发此事件
* @event {Function} popuphide 弹出的选择窗口关闭时触发此事件
*/
export default {
name: 'UniDataPicker',
emits: ['popupopened', 'popupclosed', 'nodeclick', 'input', 'change', 'update:modelValue','inputclick'],
mixins: [dataPicker],
components: {
DataPickerView
},
props: {
options: {
type: [Object, Array],
default () {
return {}
}
},
popupTitle: {
type: String,
default: '请选择'
},
placeholder: {
type: String,
default: '请选择'
},
heightMobile: {
type: String,
default: ''
},
readonly: {
type: Boolean,
default: false
},
clearIcon: {
type: Boolean,
default: true
},
border: {
type: Boolean,
default: true
},
split: {
type: String,
default: '/'
},
ellipsis: {
type: Boolean,
default: true
}
},
data() {
return {
isOpened: false,
inputSelected: []
}
},
created() {
this.$nextTick(() => {
this.load();
})
},
watch: {
localdata: {
handler() {
this.load()
},
deep: true
},
},
methods: {
clear() {
this._dispatchEvent([]);
},
onPropsChange() {
this._treeData = [];
this.selectedIndex = 0;
this.load();
},
load() {
if (this.readonly) {
this._processReadonly(this.localdata, this.dataValue);
return;
}
//
if (this.isLocalData) {
this.loadData();
this.inputSelected = this.selected.slice(0);
} else if (this.isCloudDataList || this.isCloudDataTree) { // Cloud
this.loading = true;
this.getCloudDataValue().then((res) => {
this.loading = false;
this.inputSelected = res;
}).catch((err) => {
this.loading = false;
this.errorMessage = err;
})
}
},
show() {
this.isOpened = true
setTimeout(() => {
this.$refs.pickerView.updateData({
treeData: this._treeData,
selected: this.selected,
selectedIndex: this.selectedIndex
})
}, 200)
this.$emit('popupopened')
},
hide() {
this.isOpened = false
this.$emit('popupclosed')
},
handleInput() {
if (this.readonly) {
this.$emit('inputclick')
return
}
this.show()
},
handleClose(e) {
this.hide()
},
onnodeclick(e) {
this.$emit('nodeclick', e)
},
ondatachange(e) {
this._treeData = this.$refs.pickerView._treeData
},
onchange(e) {
this.hide()
this.$nextTick(() => {
this.inputSelected = e;
})
this._dispatchEvent(e)
},
_processReadonly(dataList, value) {
var isTree = dataList.findIndex((item) => {
return item.children
})
if (isTree > -1) {
let inputValue
if (Array.isArray(value)) {
inputValue = value[value.length - 1]
if (typeof inputValue === 'object' && inputValue.value) {
inputValue = inputValue.value
}
} else {
inputValue = value
}
this.inputSelected = this._findNodePath(inputValue, this.localdata)
return
}
if (!this.hasValue) {
this.inputSelected = []
return
}
let result = []
for (let i = 0; i < value.length; i++) {
var val = value[i]
var item = dataList.find((v) => {
return v.value == val
})
if (item) {
result.push(item)
}
}
if (result.length) {
this.inputSelected = result
}
},
_filterForArray(data, valueArray) {
var result = []
for (let i = 0; i < valueArray.length; i++) {
var value = valueArray[i]
var found = data.find((item) => {
return item.value == value
})
if (found) {
result.push(found)
}
}
return result
},
_dispatchEvent(selected) {
let item = {}
if (selected.length) {
var value = new Array(selected.length)
for (var i = 0; i < selected.length; i++) {
value[i] = selected[i].value
}
item = selected[selected.length - 1]
} else {
item.value = ''
}
if (this.formItem) {
this.formItem.setValue(item.value)
}
this.$emit('input', item.value)
this.$emit('update:modelValue', item.value)
this.$emit('change', {
detail: {
value: selected
}
})
}
}
}
</script>
<style>
.uni-data-tree {
flex: 1;
position: relative;
font-size: 14px;
}
.error-text {
color: #DD524D;
}
.input-value {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
flex-wrap: nowrap;
font-size: 14px;
/* line-height: 35px; */
padding: 0 10px;
padding-right: 5px;
overflow: hidden;
height: 35px;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
.input-value-border {
border: 1px solid #e5e5e5;
border-radius: 5px;
}
.selected-area {
flex: 1;
overflow: hidden;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.load-more {
/* #ifndef APP-NVUE */
margin-right: auto;
/* #endif */
/* #ifdef APP-NVUE */
width: 40px;
/* #endif */
}
.selected-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: nowrap;
/* padding: 0 5px; */
}
.selected-item {
flex-direction: row;
/* padding: 0 1px; */
/* #ifndef APP-NVUE */
white-space: nowrap;
/* #endif */
}
.text-color {
color: #333;
}
.placeholder {
color: grey;
font-size: 12px;
}
.input-split-line {
opacity: .5;
}
.arrow-area {
position: relative;
width: 20px;
/* #ifndef APP-NVUE */
margin-bottom: 5px;
margin-left: auto;
display: flex;
/* #endif */
justify-content: center;
transform: rotate(-45deg);
transform-origin: center;
}
.input-arrow {
width: 7px;
height: 7px;
border-left: 1px solid #999;
border-bottom: 1px solid #999;
}
.uni-data-tree-cover {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .4);
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
z-index: 100;
}
.uni-data-tree-dialog {
position: fixed;
left: 0;
/* #ifndef APP-NVUE */
top: 20%;
/* #endif */
/* #ifdef APP-NVUE */
top: 200px;
/* #endif */
right: 0;
bottom: 0;
background-color: #FFFFFF;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
z-index: 102;
overflow: hidden;
/* #ifdef APP-NVUE */
width: 750rpx;
/* #endif */
}
.dialog-caption {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
/* border-bottom: 1px solid #f0f0f0; */
}
.title-area {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
/* #ifndef APP-NVUE */
margin: auto;
/* #endif */
padding: 0 10px;
}
.dialog-title {
/* font-weight: bold; */
line-height: 44px;
}
.dialog-close {
position: absolute;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 0 15px;
}
.dialog-close-plus {
width: 16px;
height: 2px;
background-color: #666;
border-radius: 2px;
transform: rotate(45deg);
}
.dialog-close-rotate {
position: absolute;
transform: rotate(-45deg);
}
.picker-view {
flex: 1;
overflow: hidden;
}
.icon-clear {
display: flex;
align-items: center;
}
/* #ifdef H5 */
@media all and (min-width: 768px) {
.uni-data-tree-cover {
background-color: transparent;
}
.uni-data-tree-dialog {
position: absolute;
top: 55px;
height: auto;
min-height: 400px;
max-height: 50vh;
background-color: #fff;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
overflow: unset;
}
.dialog-caption {
display: none;
}
.icon-clear {
/* margin-right: 5px; */
}
}
/* #endif */
/* picker 弹出层通用的指示小三角, todo扩展至上下左右方向定位 */
/* #ifndef APP-NVUE */
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-top-width: 0;
border-bottom-color: #fff;
}
/* #endif */
</style>

View File

@ -1,622 +0,0 @@
export default {
props: {
localdata: {
type: [Array, Object],
default () {
return []
}
},
spaceInfo: {
type: Object,
default () {
return {}
}
},
collection: {
type: String,
default: ''
},
action: {
type: String,
default: ''
},
field: {
type: String,
default: ''
},
orderby: {
type: String,
default: ''
},
where: {
type: [String, Object],
default: ''
},
pageData: {
type: String,
default: 'add'
},
pageCurrent: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 500
},
getcount: {
type: [Boolean, String],
default: false
},
getone: {
type: [Boolean, String],
default: false
},
gettree: {
type: [Boolean, String],
default: false
},
manual: {
type: Boolean,
default: false
},
value: {
type: [Array, String, Number],
default () {
return []
}
},
modelValue: {
type: [Array, String, Number],
default () {
return []
}
},
preload: {
type: Boolean,
default: false
},
stepSearh: {
type: Boolean,
default: true
},
selfField: {
type: String,
default: ''
},
parentField: {
type: String,
default: ''
},
multiple: {
type: Boolean,
default: false
},
map: {
type: Object,
default () {
return {
text: "text",
value: "value"
}
}
}
},
data() {
return {
loading: false,
errorMessage: '',
loadMore: {
contentdown: '',
contentrefresh: '',
contentnomore: ''
},
dataList: [],
selected: [],
selectedIndex: 0,
page: {
current: this.pageCurrent,
size: this.pageSize,
count: 0
}
}
},
computed: {
isLocalData() {
return !this.collection.length;
},
isCloudData() {
return this.collection.length > 0;
},
isCloudDataList() {
return (this.isCloudData && (!this.parentField && !this.selfField));
},
isCloudDataTree() {
return (this.isCloudData && this.parentField && this.selfField);
},
dataValue() {
let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null ||
this.modelValue !== undefined);
return isModelValue ? this.modelValue : this.value;
},
hasValue() {
if (typeof this.dataValue === 'number') {
return true
}
return (this.dataValue != null) && (this.dataValue.length > 0)
}
},
created() {
this.$watch(() => {
var al = [];
['pageCurrent',
'pageSize',
'spaceInfo',
'value',
'modelValue',
'localdata',
'collection',
'action',
'field',
'orderby',
'where',
'getont',
'getcount',
'gettree'
].forEach(key => {
al.push(this[key])
});
return al
}, (newValue, oldValue) => {
let needReset = false
for (let i = 2; i < newValue.length; i++) {
if (newValue[i] != oldValue[i]) {
needReset = true
break
}
}
if (newValue[0] != oldValue[0]) {
this.page.current = this.pageCurrent
}
this.page.size = this.pageSize
this.onPropsChange()
})
this._treeData = []
},
methods: {
onPropsChange() {
this._treeData = [];
},
// 填充 pickview 数据
async loadData() {
if (this.isLocalData) {
this.loadLocalData();
} else if (this.isCloudDataList) {
this.loadCloudDataList();
} else if (this.isCloudDataTree) {
this.loadCloudDataTree();
}
},
// 加载本地数据
async loadLocalData() {
this._treeData = [];
this._extractTree(this.localdata, this._treeData);
let inputValue = this.dataValue;
if (inputValue === undefined) {
return;
}
if (Array.isArray(inputValue)) {
inputValue = inputValue[inputValue.length - 1];
if (typeof inputValue === 'object' && inputValue[this.map.value]) {
inputValue = inputValue[this.map.value];
}
}
this.selected = this._findNodePath(inputValue, this.localdata);
},
// 加载 Cloud 数据 (单列)
async loadCloudDataList() {
if (this.loading) {
return;
}
this.loading = true;
try {
let response = await this.getCommand();
let responseData = response.result.data;
this._treeData = responseData;
this._updateBindData();
this._updateSelected();
this.onDataChange();
} catch (e) {
this.errorMessage = e;
} finally {
this.loading = false;
}
},
// 加载 Cloud 数据 (树形)
async loadCloudDataTree() {
if (this.loading) {
return;
}
this.loading = true;
try {
let commandOptions = {
field: this._cloudDataPostField(),
where: this._cloudDataTreeWhere()
};
if (this.gettree) {
commandOptions.startwith = `${this.selfField}=='${this.dataValue}'`;
}
let response = await this.getCommand(commandOptions);
let responseData = response.result.data;
this._treeData = responseData;
this._updateBindData();
this._updateSelected();
this.onDataChange();
} catch (e) {
this.errorMessage = e;
} finally {
this.loading = false;
}
},
// 加载 Cloud 数据 (节点)
async loadCloudDataNode(callback) {
if (this.loading) {
return;
}
this.loading = true;
try {
let commandOptions = {
field: this._cloudDataPostField(),
where: this._cloudDataNodeWhere()
};
let response = await this.getCommand(commandOptions);
let responseData = response.result.data;
callback(responseData);
} catch (e) {
this.errorMessage = e;
} finally {
this.loading = false;
}
},
// 回显 Cloud 数据
getCloudDataValue() {
if (this.isCloudDataList) {
return this.getCloudDataListValue();
}
if (this.isCloudDataTree) {
return this.getCloudDataTreeValue();
}
},
// 回显 Cloud 数据 (单列)
getCloudDataListValue() {
// 根据 field's as value标识匹配 where 条件
let where = [];
let whereField = this._getForeignKeyByField();
if (whereField) {
where.push(`${whereField} == '${this.dataValue}'`)
}
where = where.join(' || ');
if (this.where) {
where = `(${this.where}) && (${where})`
}
return this.getCommand({
field: this._cloudDataPostField(),
where
}).then((res) => {
this.selected = res.result.data;
return res.result.data;
});
},
// 回显 Cloud 数据 (树形)
getCloudDataTreeValue() {
return this.getCommand({
field: this._cloudDataPostField(),
getTreePath: {
startWith: `${this.selfField}=='${this.dataValue}'`
}
}).then((res) => {
let treePath = [];
this._extractTreePath(res.result.data, treePath);
this.selected = treePath;
return treePath;
});
},
getCommand(options = {}) {
/* eslint-disable no-undef */
let db = uniCloud.database(this.spaceInfo)
const action = options.action || this.action
if (action) {
db = db.action(action)
}
const collection = options.collection || this.collection
db = db.collection(collection)
const where = options.where || this.where
if (!(!where || !Object.keys(where).length)) {
db = db.where(where)
}
const field = options.field || this.field
if (field) {
db = db.field(field)
}
const orderby = options.orderby || this.orderby
if (orderby) {
db = db.orderBy(orderby)
}
const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current
const size = options.pageSize !== undefined ? options.pageSize : this.page.size
const getCount = options.getcount !== undefined ? options.getcount : this.getcount
const getTree = options.gettree !== undefined ? options.gettree : this.gettree
const getOptions = {
getCount,
getTree
}
if (options.getTreePath) {
getOptions.getTreePath = options.getTreePath
}
db = db.skip(size * (current - 1)).limit(size).get(getOptions)
return db
},
_cloudDataPostField() {
let fields = [this.field];
if (this.parentField) {
fields.push(`${this.parentField} as parent_value`);
}
return fields.join(',');
},
_cloudDataTreeWhere() {
let result = []
let selected = this.selected
let parentField = this.parentField
if (parentField) {
result.push(`${parentField} == null || ${parentField} == ""`)
}
if (selected.length) {
for (var i = 0; i < selected.length - 1; i++) {
result.push(`${parentField} == '${selected[i].value}'`)
}
}
let where = []
if (this.where) {
where.push(`(${this.where})`)
}
if (result.length) {
where.push(`(${result.join(' || ')})`)
}
return where.join(' && ')
},
_cloudDataNodeWhere() {
let where = []
let selected = this.selected;
if (selected.length) {
where.push(`${this.parentField} == '${selected[selected.length - 1].value}'`);
}
where = where.join(' || ');
if (this.where) {
return `(${this.where}) && (${where})`
}
return where
},
_getWhereByForeignKey() {
let result = []
let whereField = this._getForeignKeyByField();
if (whereField) {
result.push(`${whereField} == '${this.dataValue}'`)
}
if (this.where) {
return `(${this.where}) && (${result.join(' || ')})`
}
return result.join(' || ')
},
_getForeignKeyByField() {
let fields = this.field.split(',');
let whereField = null;
for (let i = 0; i < fields.length; i++) {
const items = fields[i].split('as');
if (items.length < 2) {
continue;
}
if (items[1].trim() === 'value') {
whereField = items[0].trim();
break;
}
}
return whereField;
},
_updateBindData(node) {
const {
dataList,
hasNodes
} = this._filterData(this._treeData, this.selected)
let isleaf = this._stepSearh === false && !hasNodes
if (node) {
node.isleaf = isleaf
}
this.dataList = dataList
this.selectedIndex = dataList.length - 1
if (!isleaf && this.selected.length < dataList.length) {
this.selected.push({
value: null,
text: "请选择"
})
}
return {
isleaf,
hasNodes
}
},
_updateSelected() {
let dl = this.dataList
let sl = this.selected
let textField = this.map.text
let valueField = this.map.value
for (let i = 0; i < sl.length; i++) {
let value = sl[i].value
let dl2 = dl[i]
for (let j = 0; j < dl2.length; j++) {
let item2 = dl2[j]
if (item2[valueField] === value) {
sl[i].text = item2[textField]
break
}
}
}
},
_filterData(data, paths) {
let dataList = []
let hasNodes = true
dataList.push(data.filter((item) => {
return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '')
}))
for (let i = 0; i < paths.length; i++) {
let value = paths[i].value
let nodes = data.filter((item) => {
return item.parent_value === value
})
if (nodes.length) {
dataList.push(nodes)
} else {
hasNodes = false
}
}
return {
dataList,
hasNodes
}
},
_extractTree(nodes, result, parent_value) {
let list = result || []
let valueField = this.map.value
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let child = {}
for (let key in node) {
if (key !== 'children') {
child[key] = node[key]
}
}
if (parent_value !== null && parent_value !== undefined && parent_value !== '') {
child.parent_value = parent_value
}
result.push(child)
let children = node.children
if (children) {
this._extractTree(children, result, node[valueField])
}
}
},
_extractTreePath(nodes, result) {
let list = result || []
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let child = {}
for (let key in node) {
if (key !== 'children') {
child[key] = node[key]
}
}
result.push(child)
let children = node.children
if (children) {
this._extractTreePath(children, result)
}
}
},
_findNodePath(key, nodes, path = []) {
let textField = this.map.text
let valueField = this.map.value
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
let children = node.children
let text = node[textField]
let value = node[valueField]
path.push({
value,
text
})
if (value === key) {
return path
}
if (children) {
const p = this._findNodePath(key, children, path)
if (p.length) {
return p
}
}
path.pop()
}
return []
}
}
}

View File

@ -1,692 +0,0 @@
export type PaginationType = {
current : number,
size : number,
count : number
}
export type LoadMoreType = {
contentdown : string,
contentrefresh : string,
contentnomore : string
}
export type SelectedItemType = {
name : string,
value : string,
}
export type GetCommandOptions = {
collection ?: UTSJSONObject,
field ?: string,
orderby ?: string,
where ?: any,
pageData ?: string,
pageCurrent ?: number,
pageSize ?: number,
getCount ?: boolean,
getTree ?: any,
getTreePath ?: UTSJSONObject,
startwith ?: string,
limitlevel ?: number,
groupby ?: string,
groupField ?: string,
distinct ?: boolean,
pageIndistinct ?: boolean,
foreignKey ?: string,
loadtime ?: string,
manual ?: boolean
}
const DefaultSelectedNode = {
text: '请选择',
value: ''
}
export const dataPicker = defineMixin({
props: {
localdata: {
type: Array as PropType<Array<UTSJSONObject>>,
default: [] as Array<UTSJSONObject>
},
collection: {
type: Object,
default: ''
},
field: {
type: String,
default: ''
},
orderby: {
type: String,
default: ''
},
where: {
type: Object,
default: ''
},
pageData: {
type: String,
default: 'add'
},
pageCurrent: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 20
},
getcount: {
type: Boolean,
default: false
},
gettree: {
type: Object,
default: ''
},
gettreepath: {
type: Object,
default: ''
},
startwith: {
type: String,
default: ''
},
limitlevel: {
type: Number,
default: 10
},
groupby: {
type: String,
default: ''
},
groupField: {
type: String,
default: ''
},
distinct: {
type: Boolean,
default: false
},
pageIndistinct: {
type: Boolean,
default: false
},
foreignKey: {
type: String,
default: ''
},
loadtime: {
type: String,
default: 'auto'
},
manual: {
type: Boolean,
default: false
},
preload: {
type: Boolean,
default: false
},
stepSearh: {
type: Boolean,
default: true
},
selfField: {
type: String,
default: ''
},
parentField: {
type: String,
default: ''
},
multiple: {
type: Boolean,
default: false
},
value: {
type: Object,
default: ''
},
modelValue: {
type: Object,
default: ''
},
defaultProps: {
type: Object as PropType<UTSJSONObject>,
}
},
data() {
return {
loading: false,
error: null as UniCloudError | null,
treeData: [] as Array<UTSJSONObject>,
selectedIndex: 0,
selectedNodes: [] as Array<UTSJSONObject>,
selectedPages: [] as Array<UTSJSONObject>[],
selectedValue: '',
selectedPaths: [] as Array<UTSJSONObject>,
pagination: {
current: 1,
size: 20,
count: 0
} as PaginationType
}
},
computed: {
mappingTextName() : string {
// TODO
return (this.defaultProps != null) ? this.defaultProps!.getString('text', 'text') : 'text'
},
mappingValueName() : string {
// TODO
return (this.defaultProps != null) ? this.defaultProps!.getString('value', 'value') : 'value'
},
currentDataList() : Array<UTSJSONObject> {
if (this.selectedIndex > this.selectedPages.length - 1) {
return [] as Array<UTSJSONObject>
}
return this.selectedPages[this.selectedIndex]
},
isLocalData() : boolean {
return this.localdata.length > 0
},
isCloudData() : boolean {
return this._checkIsNotNull(this.collection)
},
isCloudDataList() : boolean {
return (this.isCloudData && (this.parentField.length == 0 && this.selfField.length == 0))
},
isCloudDataTree() : boolean {
return (this.isCloudData && this.parentField.length > 0 && this.selfField.length > 0)
},
dataValue() : any {
return this.hasModelValue ? this.modelValue : this.value
},
hasCloudTreeData() : boolean {
return this.treeData.length > 0
},
hasModelValue() : boolean {
if (typeof this.modelValue == 'string') {
const valueString = this.modelValue as string
return (valueString.length > 0)
} else if (Array.isArray(this.modelValue)) {
const valueArray = this.modelValue as Array<string>
return (valueArray.length > 0)
}
return false
},
hasCloudDataValue() : boolean {
if (typeof this.dataValue == 'string') {
const valueString = this.dataValue as string
return (valueString.length > 0)
}
return false
}
},
created() {
this.pagination.current = this.pageCurrent
this.pagination.size = this.pageSize
this.$watch(
() : any => [
this.pageCurrent,
this.pageSize,
this.localdata,
this.value,
this.collection,
this.field,
this.getcount,
this.orderby,
this.where,
this.groupby,
this.groupField,
this.distinct
],
(newValue : Array<any>, oldValue : Array<any>) => {
this.pagination.size = this.pageSize
if (newValue[0] !== oldValue[0]) {
this.pagination.current = this.pageCurrent
}
this.onPropsChange()
}
)
},
methods: {
onPropsChange() {
this.selectedIndex = 0
this.selectedNodes.length = 0
this.selectedPages.length = 0
this.selectedPaths.length = 0
// 加载数据
this.$nextTick(() => {
this.loadData()
})
},
onTabSelect(index : number) {
this.selectedIndex = index
},
onNodeClick(nodeData : UTSJSONObject) {
if (nodeData.getBoolean('disable', false)) {
return
}
const isLeaf = this._checkIsLeafNode(nodeData)
this._trimSelectedNodes(nodeData)
this.$emit('nodeclick', nodeData)
if (this.isLocalData) {
if (isLeaf || !this._checkHasChildren(nodeData)) {
this.onFinish()
}
} else if (this.isCloudDataList) {
this.onFinish()
} else if (this.isCloudDataTree) {
if (isLeaf) {
this.onFinish()
} else if (!this._checkHasChildren(nodeData)) {
// 尝试请求一次,如果没有返回数据标记为叶子节点
this.loadCloudDataNode(nodeData)
}
}
},
getChangeNodes(): Array<UTSJSONObject> {
const nodes: Array<UTSJSONObject> = []
this.selectedNodes.forEach((node : UTSJSONObject) => {
const newNode: UTSJSONObject = {}
newNode[this.mappingTextName] = node.getString(this.mappingTextName)
newNode[this.mappingValueName] = node.getString(this.mappingValueName)
nodes.push(newNode)
})
return nodes
},
onFinish() { },
// 加载数据(自动判定环境)
loadData() {
if (this.isLocalData) {
this.loadLocalData()
} else if (this.isCloudDataList) {
this.loadCloudDataList()
} else if (this.isCloudDataTree) {
this.loadCloudDataTree()
}
},
// 加载本地数据
loadLocalData() {
this.treeData = this.localdata
if (Array.isArray(this.dataValue)) {
const value = this.dataValue as Array<UTSJSONObject>
this.selectedPaths = value.slice(0)
this._pushSelectedTreeNodes(value, this.localdata)
} else {
this._pushSelectedNodes(this.localdata)
}
},
// 加载 Cloud 数据 (单列)
loadCloudDataList() {
this._loadCloudData(null, (data : Array<UTSJSONObject>) => {
this.treeData = data
this._pushSelectedNodes(data)
})
},
// 加载 Cloud 数据 (树形)
loadCloudDataTree() {
let commandOptions = {
field: this._cloudDataPostField(),
where: this._cloudDataTreeWhere(),
getTree: true
} as GetCommandOptions
if (this._checkIsNotNull(this.gettree)) {
commandOptions.startwith = `${this.selfField}=='${this.dataValue as string}'`
}
this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => {
this.treeData = data
if (this.selectedPaths.length > 0) {
this._pushSelectedTreeNodes(this.selectedPaths, data)
} else {
this._pushSelectedNodes(data)
}
})
},
// 加载 Cloud 数据 (节点)
loadCloudDataNode(nodeData : UTSJSONObject) {
const commandOptions = {
field: this._cloudDataPostField(),
where: this._cloudDataNodeWhere()
} as GetCommandOptions
this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => {
nodeData['children'] = data
if (data.length == 0) {
nodeData['isleaf'] = true
this.onFinish()
} else {
this._pushSelectedNodes(data)
}
})
},
// 回显 Cloud Tree Path
loadCloudDataPath() {
if (!this.hasCloudDataValue) {
return
}
const command : GetCommandOptions = {}
// 单列
if (this.isCloudDataList) {
// 根据 field's as value标识匹配 where 条件
let where : Array<string> = [];
let whereField = this._getForeignKeyByField();
if (whereField.length > 0) {
where.push(`${whereField} == '${this.dataValue as string}'`)
}
let whereString = where.join(' || ')
if (this._checkIsNotNull(this.where)) {
whereString = `(${this.where}) && (${whereString})`
}
command.field = this._cloudDataPostField()
command.where = whereString
}
// 树形
if (this.isCloudDataTree) {
command.field = this._cloudDataPostField()
command.getTreePath = {
startWith: `${this.selfField}=='${this.dataValue as string}'`
}
}
this._loadCloudData(command, (data : Array<UTSJSONObject>) => {
this._extractTreePath(data, this.selectedPaths)
})
},
_loadCloudData(options ?: GetCommandOptions, callback ?: ((data : Array<UTSJSONObject>) => void)) {
if (this.loading) {
return
}
this.loading = true
this.error = null
this._getCommand(options).then((response : UniCloudDBGetResult) => {
callback?.(response.data)
}).catch((err : any | null) => {
this.error = err as UniCloudError
}).finally(() => {
this.loading = false
})
},
_cloudDataPostField() : string {
let fields = [this.field];
if (this.parentField.length > 0) {
fields.push(`${this.parentField} as parent_value`)
}
return fields.join(',')
},
_cloudDataTreeWhere() : string {
let result : Array<string> = []
let selectedNodes = this.selectedNodes.length > 0 ? this.selectedNodes : this.selectedPaths
let parentField = this.parentField
if (parentField.length > 0) {
result.push(`${parentField} == null || ${parentField} == ""`)
}
if (selectedNodes.length > 0) {
for (var i = 0; i < selectedNodes.length - 1; i++) {
const parentFieldValue = selectedNodes[i].getString('value', '')
result.push(`${parentField} == '${parentFieldValue}'`)
}
}
let where : Array<string> = []
if (this._checkIsNotNull(this.where)) {
where.push(`(${this.where as string})`)
}
if (result.length > 0) {
where.push(`(${result.join(' || ')})`)
}
return where.join(' && ')
},
_cloudDataNodeWhere() : string {
const where : Array<string> = []
if (this.selectedNodes.length > 0) {
const value = this.selectedNodes[this.selectedNodes.length - 1].getString('value', '')
where.push(`${this.parentField} == '${value}'`)
}
let whereString = where.join(' || ')
if (this._checkIsNotNull(this.where)) {
return `(${this.where as string}) && (${whereString})`
}
return whereString
},
_getWhereByForeignKey() : string {
let result : Array<string> = []
let whereField = this._getForeignKeyByField();
if (whereField.length > 0) {
result.push(`${whereField} == '${this.dataValue as string}'`)
}
if (this._checkIsNotNull(this.where)) {
return `(${this.where}) && (${result.join(' || ')})`
}
return result.join(' || ')
},
_getForeignKeyByField() : string {
const fields = this.field.split(',')
let whereField = ''
for (let i = 0; i < fields.length; i++) {
const items = fields[i].split('as')
if (items.length < 2) {
continue
}
if (items[1].trim() === 'value') {
whereField = items[0].trim()
break
}
}
return whereField
},
_getCommand(options ?: GetCommandOptions) : Promise<UniCloudDBGetResult> {
let db = uniCloud.databaseForJQL()
let collection = Array.isArray(this.collection) ? db.collection(...(this.collection as Array<any>)) : db.collection(this.collection)
let filter : UniCloudDBFilter | null = null
if (this.foreignKey.length > 0) {
filter = collection.foreignKey(this.foreignKey)
}
const where : any = options?.where ?? this.where
if (typeof where == 'string') {
const whereString = where as string
if (whereString.length > 0) {
filter = (filter != null) ? filter.where(where) : collection.where(where)
}
} else {
filter = (filter != null) ? filter.where(where) : collection.where(where)
}
let query : UniCloudDBQuery | null = null
if (this.field.length > 0) {
query = (filter != null) ? filter.field(this.field) : collection.field(this.field)
}
if (this.groupby.length > 0) {
if (query != null) {
query = query.groupBy(this.groupby)
} else if (filter != null) {
query = filter.groupBy(this.groupby)
}
}
if (this.groupField.length > 0) {
if (query != null) {
query = query.groupField(this.groupField)
} else if (filter != null) {
query = filter.groupField(this.groupField)
}
}
if (this.distinct == true) {
if (query != null) {
query = query.distinct(this.field)
} else if (filter != null) {
query = filter.distinct(this.field)
}
}
if (this.orderby.length > 0) {
if (query != null) {
query = query.orderBy(this.orderby)
} else if (filter != null) {
query = filter.orderBy(this.orderby)
}
}
const size = this.pagination.size
const current = this.pagination.current
if (query != null) {
query = query.skip(size * (current - 1)).limit(size)
} else if (filter != null) {
query = filter.skip(size * (current - 1)).limit(size)
} else {
query = collection.skip(size * (current - 1)).limit(size)
}
const getOptions = {}
const treeOptions = {
limitLevel: this.limitlevel,
startWith: this.startwith
}
if (this.getcount == true) {
getOptions['getCount'] = this.getcount
}
const getTree : any = options?.getTree ?? this.gettree
if (typeof getTree == 'string') {
const getTreeString = getTree as string
if (getTreeString.length > 0) {
getOptions['getTree'] = treeOptions
}
} else if (typeof getTree == 'object') {
getOptions['getTree'] = treeOptions
} else {
getOptions['getTree'] = getTree
}
const getTreePath = options?.getTreePath ?? this.gettreepath
if (typeof getTreePath == 'string') {
const getTreePathString = getTreePath as string
if (getTreePathString.length > 0) {
getOptions['getTreePath'] = getTreePath
}
} else {
getOptions['getTreePath'] = getTreePath
}
return query.get(getOptions)
},
_checkIsNotNull(value : any) : boolean {
if (typeof value == 'string') {
const valueString = value as string
return (valueString.length > 0)
} else if (value instanceof UTSJSONObject) {
return true
}
return false
},
_checkIsLeafNode(nodeData : UTSJSONObject) : boolean {
if (this.selectedIndex >= this.limitlevel) {
return true
}
if (nodeData.getBoolean('isleaf', false)) {
return true
}
return false
},
_checkHasChildren(nodeData : UTSJSONObject) : boolean {
const children = nodeData.getArray('children') ?? ([] as Array<any>)
return children.length > 0
},
_pushSelectedNodes(nodes : Array<UTSJSONObject>) {
this.selectedNodes.push(DefaultSelectedNode)
this.selectedPages.push(nodes)
this.selectedIndex = this.selectedPages.length - 1
},
_trimSelectedNodes(nodeData : UTSJSONObject) {
this.selectedNodes.splice(this.selectedIndex)
this.selectedNodes.push(nodeData)
if (this.selectedPages.length > 0) {
this.selectedPages.splice(this.selectedIndex + 1)
}
const children = nodeData.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>)
if (children.length > 0) {
this.selectedNodes.push(DefaultSelectedNode)
this.selectedPages.push(children)
}
this.selectedIndex = this.selectedPages.length - 1
},
_pushSelectedTreeNodes(paths : Array<UTSJSONObject>, nodes : Array<UTSJSONObject>) {
let children : Array<UTSJSONObject> = nodes
paths.forEach((node : UTSJSONObject) => {
const findNode = children.find((item : UTSJSONObject) : boolean => {
return (item.getString(this.mappingValueName) == node.getString(this.mappingValueName))
})
if (findNode != null) {
this.selectedPages.push(children)
this.selectedNodes.push(node)
children = findNode.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>)
}
})
this.selectedIndex = this.selectedPages.length - 1
},
_extractTreePath(nodes : Array<UTSJSONObject>, result : Array<UTSJSONObject>) {
if (nodes.length == 0) {
return
}
const node = nodes[0]
result.push(node)
const children = node.getArray<UTSJSONObject>('children')
if (Array.isArray(children) && children!.length > 0) {
this._extractTreePath(children, result)
}
}
}
})

View File

@ -1,76 +0,0 @@
.uni-data-pickerview {
position: relative;
flex-direction: column;
overflow: hidden;
}
.loading-cover {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
align-items: center;
justify-content: center;
background-color: rgba(150, 150, 150, .1);
}
.error {
background-color: #fff;
padding: 15px;
}
.error-text {
color: #DD524D;
}
.selected-node-list {
flex-direction: row;
flex-wrap: nowrap;
}
.selected-node-item {
margin-left: 10px;
margin-right: 10px;
padding: 8px 10px 8px 10px;
border-bottom: 2px solid transparent;
}
.selected-node-item-active {
color: #007aff;
border-bottom-color: #007aff;
}
.list-view {
flex: 1;
}
.list-item {
flex-direction: row;
justify-content: space-between;
padding: 12px 15px;
border-bottom: 1px solid #f0f0f0;
}
.item-text {
color: #333333;
}
.item-text-disabled {
opacity: .5;
}
.item-text-overflow {
overflow: hidden;
}
.check {
margin-right: 5px;
border: 2px solid #007aff;
border-left: 0;
border-top: 0;
height: 12px;
width: 6px;
transform-origin: center;
transform: rotate(45deg);
}

View File

@ -1,69 +0,0 @@
<template>
<view class="uni-data-pickerview">
<view v-if="error!=null" class="error">
<text class="error-text">{{error!.errMsg}}</text>
</view>
<scroll-view v-if="!isCloudDataList" :scroll-x="true">
<view class="selected-node-list">
<template v-for="(item, index) in selectedNodes">
<text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}"
@click="onTabSelect(index)">
{{item[mappingTextName]}}
</text>
</template>
</view>
</scroll-view>
<list-view class="list-view" :scroll-y="true">
<list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)">
<text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text>
<text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text>
</list-item>
</list-view>
<view class="loading-cover" v-if="loading">
<slot name="pickerview-loading" :loading="loading"></slot>
</view>
</view>
</template>
<script>
import { dataPicker } from "./uni-data-picker.uts"
/**
* DataPickerview
* @description uni-data-pickerview
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {Array} localdata 本地数据,参考
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询,仅查询当前选中节点
* @value false 关闭分布查询,一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
*/
export default {
name: 'UniDataPickerView',
emits: ['nodeclick', 'change', 'update:modelValue'],
mixins: [dataPicker],
props: {
ellipsis: {
type: Boolean,
default: true
}
},
created() {
this.loadData()
},
methods: {
onFinish() {
this.$emit('change', this.getChangeNodes())
}
}
}
</script>
<style>
@import url("uni-data-pickerview.css");
</style>

View File

@ -1,323 +0,0 @@
<template>
<view class="uni-data-pickerview">
<scroll-view v-if="!isCloudDataList" class="selected-area" scroll-x="true">
<view class="selected-list">
<view
class="selected-item"
v-for="(item,index) in selected"
:key="index"
:class="{
'selected-item-active':index == selectedIndex
}"
@click="handleSelect(index)"
>
<text>{{item.text || ''}}</text>
</view>
</view>
</scroll-view>
<view class="tab-c">
<scroll-view class="list" :scroll-y="true">
<view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in dataList[selectedIndex]" :key="j"
@click="handleNodeClick(item, selectedIndex, j)">
<text class="item-text">{{item[map.text]}}</text>
<view class="check" v-if="selected.length > selectedIndex && item[map.value] == selected[selectedIndex].value"></view>
</view>
</scroll-view>
<view class="loading-cover" v-if="loading">
<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
</view>
<view class="error-message" v-if="errorMessage">
<text class="error-text">{{errorMessage}}</text>
</view>
</view>
</view>
</template>
<script>
import dataPicker from "./uni-data-picker.js"
/**
* DataPickerview
* @description uni-data-pickerview
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796
* @property {Array} localdata 本地数据参考
* @property {Boolean} step-searh = [true|false] 是否分布查询
* @value true 启用分布查询仅查询当前选中节点
* @value false 关闭分布查询一次查询出所有数据
* @property {String|DBFieldString} self-field 分布查询当前字段名称
* @property {String|DBFieldString} parent-field 分布查询父字段名称
* @property {String|DBCollectionString} collection 表名
* @property {String|DBFieldString} field 查询字段多个字段用 `,` 分割
* @property {String} orderby 排序字段及正序倒叙设置
* @property {String|JQLString} where 查询条件
*/
export default {
name: 'UniDataPickerView',
emits: ['nodeclick', 'change', 'datachange', 'update:modelValue'],
mixins: [dataPicker],
props: {
managedMode: {
type: Boolean,
default: false
},
ellipsis: {
type: Boolean,
default: true
}
},
created() {
if (!this.managedMode) {
this.$nextTick(() => {
this.loadData();
})
}
},
methods: {
onPropsChange() {
this._treeData = [];
this.selectedIndex = 0;
this.$nextTick(() => {
this.loadData();
})
},
handleSelect(index) {
this.selectedIndex = index;
},
handleNodeClick(item, i, j) {
if (item.disable) {
return;
}
const node = this.dataList[i][j];
const text = node[this.map.text];
const value = node[this.map.value];
if (i < this.selected.length - 1) {
this.selected.splice(i, this.selected.length - i)
this.selected.push({
text,
value
})
} else if (i === this.selected.length - 1) {
this.selected.splice(i, 1, {
text,
value
})
}
if (node.isleaf) {
this.onSelectedChange(node, node.isleaf)
return
}
const {
isleaf,
hasNodes
} = this._updateBindData()
//
if (this.isLocalData) {
this.onSelectedChange(node, (!hasNodes || isleaf))
} else if (this.isCloudDataList) { // Cloud ()
this.onSelectedChange(node, true)
} else if (this.isCloudDataTree) { // Cloud ()
if (isleaf) {
this.onSelectedChange(node, node.isleaf)
} else if (!hasNodes) { //
this.loadCloudDataNode((data) => {
if (!data.length) {
node.isleaf = true
} else {
this._treeData.push(...data)
this._updateBindData(node)
}
this.onSelectedChange(node, node.isleaf)
})
}
}
},
updateData(data) {
this._treeData = data.treeData
this.selected = data.selected
if (!this._treeData.length) {
this.loadData()
} else {
//this.selected = data.selected
this._updateBindData()
}
},
onDataChange() {
this.$emit('datachange');
},
onSelectedChange(node, isleaf) {
if (isleaf) {
this._dispatchEvent()
}
if (node) {
this.$emit('nodeclick', node)
}
},
_dispatchEvent() {
this.$emit('change', this.selected.slice(0))
}
}
}
</script>
<style lang="scss">
$uni-primary: #007aff !default;
.uni-data-pickerview {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
overflow: hidden;
height: 100%;
}
.error-text {
color: #DD524D;
}
.loading-cover {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, .5);
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
z-index: 1001;
}
.load-more {
/* #ifndef APP-NVUE */
margin: auto;
/* #endif */
}
.error-message {
background-color: #fff;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
padding: 15px;
opacity: .9;
z-index: 102;
}
/* #ifdef APP-NVUE */
.selected-area {
width: 750rpx;
}
/* #endif */
.selected-list {
/* #ifndef APP-NVUE */
display: flex;
flex-wrap: nowrap;
/* #endif */
flex-direction: row;
padding: 0 5px;
border-bottom: 1px solid #f8f8f8;
}
.selected-item {
margin-left: 10px;
margin-right: 10px;
padding: 12px 0;
text-align: center;
/* #ifndef APP-NVUE */
white-space: nowrap;
/* #endif */
}
.selected-item-text-overflow {
width: 168px;
/* fix nvue */
overflow: hidden;
/* #ifndef APP-NVUE */
width: 6em;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
/* #endif */
}
.selected-item-active {
border-bottom: 2px solid $uni-primary;
}
.selected-item-text {
color: $uni-primary;
}
.tab-c {
position: relative;
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
overflow: hidden;
}
.list {
flex: 1;
}
.item {
padding: 12px 15px;
/* border-bottom: 1px solid #f0f0f0; */
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
}
.is-disabled {
opacity: .5;
}
.item-text {
/* flex: 1; */
color: #333333;
}
.item-text-overflow {
width: 280px;
/* fix nvue */
overflow: hidden;
/* #ifndef APP-NVUE */
width: 20em;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
/* #endif */
}
.check {
margin-right: 5px;
border: 2px solid $uni-primary;
border-left: 0;
border-top: 0;
height: 12px;
width: 6px;
transform-origin: center;
/* #ifndef APP-NVUE */
transition: all 0.3s;
/* #endif */
transform: rotate(45deg);
}
</style>

View File

@ -1,91 +0,0 @@
{
"id": "uni-data-picker",
"displayName": "uni-data-picker 数据驱动的picker选择器",
"version": "2.0.1",
"description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景",
"keywords": [
"uni-ui",
"uniui",
"picker",
"级联",
"省市区",
""
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"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"
},
"uni_modules": {
"dependencies": [
"uni-load-more",
"uni-icons",
"uni-scss"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,22 +0,0 @@
## DataPicker 级联选择
> **组件名uni-data-picker**
> 代码块: `uDataPicker`
> 关联组件:`uni-data-pickerview`、`uni-load-more`。
`<uni-data-picker>` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。
支持单列、和多列级联选择。列数没有限制如果屏幕显示不全顶部tab区域会左右滚动。
候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。
`<uni-data-picker>` 组件尤其适用于地址选择、分类选择等选择类。
`<uni-data-picker>` 支持本地数据、云端静态数据(json)uniCloud云数据库数据。
`<uni-data-picker>` 可以通过JQL直连uniCloud云数据库配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema)可在schema2code中自动生成前端页面还支持服务器端校验。
在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面会自动生成地址管理的维护页面自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,39 +0,0 @@
## 1.0.82024-03-28
- 修复 在vue2下:style动态绑定导致编译失败的bug
## 1.0.72024-01-20
- 修复 长文本回显超过容器的bug超过容器部分显示省略号
## 1.0.62023-04-12
- 修复 微信小程序点击时会改变背景颜色的 bug
## 1.0.52023-02-03
- 修复 禁用时会显示清空按钮
## 1.0.42023-02-02
- 优化 查询条件短期内多次变更只查询最后一次变更后的结果
- 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue
## 1.0.32023-01-16
- 修复 不关联服务空间报错的问题
## 1.0.22023-01-14
- 新增 属性 `format` 可用于格式化显示选项内容
## 1.0.12022-12-06
- 修复 当where变化时数据不会自动更新的问题
## 0.1.92022-09-05
- 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框
## 0.1.82022-08-29
- 修复 点击的位置不准确
## 0.1.72022-08-12
- 新增 支持 disabled 属性
## 0.1.62022-07-06
- 修复 pc端宽度异常的bug
## 0.1.5
- 修复 pc端宽度异常的bug
## 0.1.42022-07-05
- 优化 显示样式
## 0.1.32022-06-02
- 修复 localdata 赋值不生效的 bug
- 新增 支持 uni.scss 修改颜色
- 新增 支持选项禁用(数据选项设置 disabled: true 即禁用)
## 0.1.22022-05-08
- 修复 当 value 为 0 时选择不生效的 bug
## 0.1.12022-05-07
- 新增 记住上次的选项(仅 collection 存在时有效)
## 0.1.02022-04-22
- 初始化

View File

@ -1,562 +0,0 @@
<template>
<view class="uni-stat__select">
<span v-if="label" class="uni-label-text hide-on-phone">{{label + ''}}</span>
<view class="uni-stat-box" :class="{'uni-stat__actived': current}">
<view class="uni-select" :class="{'uni-select--disabled':disabled}">
<view class="uni-select__input-box" @click="toggleSelector">
<view v-if="current" class="uni-select__input-text">{{textShow}}</view>
<view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view>
<view v-if="current && clear && !disabled" @click.stop="clearVal">
<uni-icons type="clear" color="#c0c4cc" size="24" />
</view>
<view v-else>
<uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" />
</view>
</view>
<view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" />
<view class="uni-select__selector" :style="getOffsetByPlacement" v-if="showSelector">
<view :class="placement=='bottom'?'uni-popper__arrow_bottom':'uni-popper__arrow_top'"></view>
<scroll-view scroll-y="true" class="uni-select__selector-scroll">
<view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0">
<text>{{emptyTips}}</text>
</view>
<view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index"
@click="change(item)">
<text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text>
</view>
</scroll-view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* DataChecklist 数据选择器
* @description 通过数据渲染的下拉框组件
* @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select
* @property {String} value 默认值
* @property {Array} localdata 本地数据 格式 [{text:'',value:''}]
* @property {Boolean} clear 是否可以清空已选项
* @property {Boolean} emptyText 没有数据时显示的文字 本地数据无效
* @property {String} label 左侧标题
* @property {String} placeholder 输入框的提示文字
* @property {Boolean} disabled 是否禁用
* @property {String} placement 弹出位置
* @value top 顶部弹出
* @value bottom 底部弹出default)
* @event {Function} change 选中发生变化触发
*/
export default {
name: "uni-data-select",
mixins: [uniCloud.mixinDatacom || {}],
props: {
localdata: {
type: Array,
default () {
return []
}
},
value: {
type: [String, Number],
default: ''
},
modelValue: {
type: [String, Number],
default: ''
},
label: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
emptyTips: {
type: String,
default: '无选项'
},
clear: {
type: Boolean,
default: true
},
defItem: {
type: Number,
default: 0
},
disabled: {
type: Boolean,
default: false
},
// field="_id as value, version as text, uni_platform as label" format="{label} - {text}"
format: {
type: String,
default: ''
},
placement: {
type: String,
default: 'bottom'
}
},
data() {
return {
showSelector: false,
current: '',
mixinDatacomResData: [],
apps: [],
channels: [],
cacheKey: "uni-data-select-lastSelectedValue",
};
},
created() {
this.debounceGet = this.debounce(() => {
this.query();
}, 300);
if (this.collection && !this.localdata.length) {
this.debounceGet();
}
},
computed: {
typePlaceholder() {
const text = {
'opendb-stat-app-versions': '版本',
'opendb-app-channels': '渠道',
'opendb-app-list': '应用'
}
const common = this.placeholder
const placeholder = text[this.collection]
return placeholder ?
common + placeholder :
common
},
valueCom() {
// #ifdef VUE3
return this.modelValue;
// #endif
// #ifndef VUE3
return this.value;
// #endif
},
textShow() {
//
let text = this.current;
if (text.length > 10) {
return text.slice(0, 25) + '...';
}
return text;
},
getOffsetByPlacement() {
switch (this.placement) {
case 'top':
return "bottom:calc(100% + 12px);";
case 'bottom':
return "top:calc(100% + 12px);";
}
}
},
watch: {
localdata: {
immediate: true,
handler(val, old) {
if (Array.isArray(val) && old !== val) {
this.mixinDatacomResData = val
}
}
},
valueCom(val, old) {
this.initDefVal()
},
mixinDatacomResData: {
immediate: true,
handler(val) {
if (val.length) {
this.initDefVal()
}
}
},
},
methods: {
debounce(fn, time = 100) {
let timer = null
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, time)
}
},
//
query() {
this.mixinDatacomEasyGet();
},
//
onMixinDatacomPropsChange() {
if (this.collection) {
this.debounceGet();
}
},
initDefVal() {
let defValue = ''
if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) {
defValue = this.valueCom
} else {
let strogeValue
if (this.collection) {
strogeValue = this.getCache()
}
if (strogeValue || strogeValue === 0) {
defValue = strogeValue
} else {
let defItem = ''
if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) {
defItem = this.mixinDatacomResData[this.defItem - 1].value
}
defValue = defItem
}
if (defValue || defValue === 0) {
this.emit(defValue)
}
}
const def = this.mixinDatacomResData.find(item => item.value === defValue)
this.current = def ? this.formatItemName(def) : ''
},
/**
* @param {[String, Number]} value
* 判断用户给的 value 是否同时为禁用状态
*/
isDisabled(value) {
let isDisabled = false;
this.mixinDatacomResData.forEach(item => {
if (item.value === value) {
isDisabled = item.disable
}
})
return isDisabled;
},
clearVal() {
this.emit('')
if (this.collection) {
this.removeCache()
}
},
change(item) {
if (!item.disable) {
this.showSelector = false
this.current = this.formatItemName(item)
this.emit(item.value)
}
},
emit(val) {
this.$emit('input', val)
this.$emit('update:modelValue', val)
this.$emit('change', val)
if (this.collection) {
this.setCache(val);
}
},
toggleSelector() {
if (this.disabled) {
return
}
this.showSelector = !this.showSelector
},
formatItemName(item) {
let {
text,
value,
channel_code
} = item
channel_code = channel_code ? `(${channel_code})` : ''
if (this.format) {
//
let str = "";
str = this.format;
for (let key in item) {
str = str.replace(new RegExp(`{${key}}`, "g"), item[key]);
}
return str;
} else {
return this.collection.indexOf('app-list') > 0 ?
`${text}(${value})` :
(
text ?
text :
`未命名${channel_code}`
)
}
},
//
getLoadData() {
return this.mixinDatacomResData;
},
// key
getCurrentCacheKey() {
return this.collection;
},
//
getCache(name = this.getCurrentCacheKey()) {
let cacheData = uni.getStorageSync(this.cacheKey) || {};
return cacheData[name];
},
//
setCache(value, name = this.getCurrentCacheKey()) {
let cacheData = uni.getStorageSync(this.cacheKey) || {};
cacheData[name] = value;
uni.setStorageSync(this.cacheKey, cacheData);
},
//
removeCache(name = this.getCurrentCacheKey()) {
let cacheData = uni.getStorageSync(this.cacheKey) || {};
delete cacheData[name];
uni.setStorageSync(this.cacheKey, cacheData);
},
}
}
</script>
<style lang="scss">
$uni-base-color: #6a6a6a !default;
$uni-main-color: #333 !default;
$uni-secondary-color: #909399 !default;
$uni-border-3: #e5e5e5;
/* #ifndef APP-NVUE */
@media screen and (max-width: 500px) {
.hide-on-phone {
display: none;
}
}
/* #endif */
.uni-stat__select {
display: flex;
align-items: center;
// padding: 15px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
width: 100%;
flex: 1;
box-sizing: border-box;
}
.uni-stat-box {
width: 100%;
flex: 1;
}
.uni-stat__actived {
width: 100%;
flex: 1;
// outline: 1px solid #2979ff;
}
.uni-label-text {
font-size: 14px;
font-weight: bold;
color: $uni-base-color;
margin: auto 0;
margin-right: 5px;
}
.uni-select {
font-size: 14px;
border: 1px solid $uni-border-3;
box-sizing: border-box;
border-radius: 4px;
padding: 0 5px;
padding-left: 10px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
user-select: none;
/* #endif */
flex-direction: row;
align-items: center;
border-bottom: solid 1px $uni-border-3;
width: 100%;
flex: 1;
height: 35px;
&--disabled {
background-color: #f5f7fa;
cursor: not-allowed;
}
}
.uni-select__label {
font-size: 16px;
// line-height: 22px;
height: 35px;
padding-right: 10px;
color: $uni-secondary-color;
}
.uni-select__input-box {
height: 35px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
}
.uni-select__input {
flex: 1;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.uni-select__input-plac {
font-size: 14px;
color: $uni-secondary-color;
}
.uni-select__selector {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
position: absolute;
left: 0;
width: 100%;
background-color: #FFFFFF;
border: 1px solid #EBEEF5;
border-radius: 6px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 3;
padding: 4px 0;
}
.uni-select__selector-scroll {
/* #ifndef APP-NVUE */
max-height: 200px;
box-sizing: border-box;
/* #endif */
}
/* #ifdef H5 */
@media (min-width: 768px) {
.uni-select__selector-scroll {
max-height: 600px;
}
}
/* #endif */
.uni-select__selector-empty,
.uni-select__selector-item {
/* #ifndef APP-NVUE */
display: flex;
cursor: pointer;
/* #endif */
line-height: 35px;
font-size: 14px;
text-align: center;
/* border-bottom: solid 1px $uni-border-3; */
padding: 0px 10px;
}
.uni-select__selector-item:hover {
background-color: #f9f9f9;
}
.uni-select__selector-empty:last-child,
.uni-select__selector-item:last-child {
/* #ifndef APP-NVUE */
border-bottom: none;
/* #endif */
}
.uni-select__selector__disabled {
opacity: 0.4;
cursor: default;
}
/* picker 弹出层通用的指示小三角 */
.uni-popper__arrow_bottom,
.uni-popper__arrow_bottom::after,
.uni-popper__arrow_top,
.uni-popper__arrow_top::after,
{
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
.uni-popper__arrow_bottom {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow_bottom::after {
content: " ";
top: 1px;
margin-left: -6px;
border-top-width: 0;
border-bottom-color: #fff;
}
.uni-popper__arrow_top {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
bottom: -6px;
left: 10%;
margin-right: 3px;
border-bottom-width: 0;
border-top-color: #EBEEF5;
}
.uni-popper__arrow_top::after {
content: " ";
bottom: 1px;
margin-left: -6px;
border-bottom-width: 0;
border-top-color: #fff;
}
.uni-select__input-text {
// width: 280px;
width: 100%;
color: $uni-main-color;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
}
.uni-select__input-placeholder {
color: $uni-base-color;
font-size: 12px;
}
.uni-select--mask {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 2;
}
</style>

View File

@ -1,86 +0,0 @@
{
"id": "uni-data-select",
"displayName": "uni-data-select 下拉框选择器",
"version": "1.0.8",
"description": "通过数据驱动的下拉框选择器",
"keywords": [
"uni-ui",
"select",
"uni-data-select",
"下拉框",
"下拉选"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.1.1"
},
"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"
},
"uni_modules": {
"dependencies": ["uni-load-more"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "u",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,8 +0,0 @@
## DataSelect 下拉框选择器
> **组件名uni-data-select**
> 代码块: `uDataSelect`
当选项过多时,使用下拉菜单展示并选择内容
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,10 +0,0 @@
## 1.0.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-dateformat](https://uniapp.dcloud.io/component/uniui/uni-dateformat)
## 0.0.52021-07-08
- 调整 默认时间不再是当前时间,而是显示'-'字符
## 0.0.42021-05-12
- 新增 组件示例地址
## 0.0.32021-02-04
- 调整为uni_modules目录规范
- 修复 iOS 平台日期格式化出错的问题

View File

@ -1,200 +0,0 @@
// yyyy-MM-dd hh:mm:ss.SSS 所有支持的类型
function pad(str, length = 2) {
str += ''
while (str.length < length) {
str = '0' + str
}
return str.slice(-length)
}
const parser = {
yyyy: (dateObj) => {
return pad(dateObj.year, 4)
},
yy: (dateObj) => {
return pad(dateObj.year)
},
MM: (dateObj) => {
return pad(dateObj.month)
},
M: (dateObj) => {
return dateObj.month
},
dd: (dateObj) => {
return pad(dateObj.day)
},
d: (dateObj) => {
return dateObj.day
},
hh: (dateObj) => {
return pad(dateObj.hour)
},
h: (dateObj) => {
return dateObj.hour
},
mm: (dateObj) => {
return pad(dateObj.minute)
},
m: (dateObj) => {
return dateObj.minute
},
ss: (dateObj) => {
return pad(dateObj.second)
},
s: (dateObj) => {
return dateObj.second
},
SSS: (dateObj) => {
return pad(dateObj.millisecond, 3)
},
S: (dateObj) => {
return dateObj.millisecond
},
}
// 这都n年了iOS依然不认识2020-12-12需要转换为2020/12/12
function getDate(time) {
if (time instanceof Date) {
return time
}
switch (typeof time) {
case 'string':
{
// 2020-12-12T12:12:12.000Z、2020-12-12T12:12:12.000
if (time.indexOf('T') > -1) {
return new Date(time)
}
return new Date(time.replace(/-/g, '/'))
}
default:
return new Date(time)
}
}
export function formatDate(date, format = 'yyyy/MM/dd hh:mm:ss') {
if (!date && date !== 0) {
return ''
}
date = getDate(date)
const dateObj = {
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
hour: date.getHours(),
minute: date.getMinutes(),
second: date.getSeconds(),
millisecond: date.getMilliseconds()
}
const tokenRegExp = /yyyy|yy|MM|M|dd|d|hh|h|mm|m|ss|s|SSS|SS|S/
let flag = true
let result = format
while (flag) {
flag = false
result = result.replace(tokenRegExp, function(matched) {
flag = true
return parser[matched](dateObj)
})
}
return result
}
export function friendlyDate(time, {
locale = 'zh',
threshold = [60000, 3600000],
format = 'yyyy/MM/dd hh:mm:ss'
}) {
if (time === '-') {
return time
}
if (!time && time !== 0) {
return ''
}
const localeText = {
zh: {
year: '年',
month: '月',
day: '天',
hour: '小时',
minute: '分钟',
second: '秒',
ago: '前',
later: '后',
justNow: '刚刚',
soon: '马上',
template: '{num}{unit}{suffix}'
},
en: {
year: 'year',
month: 'month',
day: 'day',
hour: 'hour',
minute: 'minute',
second: 'second',
ago: 'ago',
later: 'later',
justNow: 'just now',
soon: 'soon',
template: '{num} {unit} {suffix}'
}
}
const text = localeText[locale] || localeText.zh
let date = getDate(time)
let ms = date.getTime() - Date.now()
let absMs = Math.abs(ms)
if (absMs < threshold[0]) {
return ms < 0 ? text.justNow : text.soon
}
if (absMs >= threshold[1]) {
return formatDate(date, format)
}
let num
let unit
let suffix = text.later
if (ms < 0) {
suffix = text.ago
ms = -ms
}
const seconds = Math.floor((ms) / 1000)
const minutes = Math.floor(seconds / 60)
const hours = Math.floor(minutes / 60)
const days = Math.floor(hours / 24)
const months = Math.floor(days / 30)
const years = Math.floor(months / 12)
switch (true) {
case years > 0:
num = years
unit = text.year
break
case months > 0:
num = months
unit = text.month
break
case days > 0:
num = days
unit = text.day
break
case hours > 0:
num = hours
unit = text.hour
break
case minutes > 0:
num = minutes
unit = text.minute
break
default:
num = seconds
unit = text.second
break
}
if (locale === 'en') {
if (num === 1) {
num = 'a'
} else {
unit += 's'
}
}
return text.template.replace(/{\s*num\s*}/g, num + '').replace(/{\s*unit\s*}/g, unit).replace(/{\s*suffix\s*}/g,
suffix)
}

View File

@ -1,88 +0,0 @@
<template>
<text>{{dateShow}}</text>
</template>
<script>
import {friendlyDate} from './date-format.js'
/**
* Dateformat 日期格式化
* @description 日期格式化组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=3279
* @property {Object|String|Number} date 日期对象/日期字符串/时间戳
* @property {String} locale 格式化使用的语言
* @value zh 中文
* @value en 英文
* @property {Array} threshold 应用不同类型格式化的阈值
* @property {String} format 输出日期字符串时的格式
*/
export default {
name: 'uniDateformat',
props: {
date: {
type: [Object, String, Number],
default () {
return '-'
}
},
locale: {
type: String,
default: 'zh',
},
threshold: {
type: Array,
default () {
return [0, 0]
}
},
format: {
type: String,
default: 'yyyy/MM/dd hh:mm:ss'
},
// refreshRate使使
refreshRate: {
type: [Number, String],
default: 0
}
},
data() {
return {
refreshMark: 0
}
},
computed: {
dateShow() {
this.refreshMark
return friendlyDate(this.date, {
locale: this.locale,
threshold: this.threshold,
format: this.format
})
}
},
watch: {
refreshRate: {
handler() {
this.setAutoRefresh()
},
immediate: true
}
},
methods: {
refresh() {
this.refreshMark++
},
setAutoRefresh() {
clearInterval(this.refreshInterval)
if (this.refreshRate) {
this.refreshInterval = setInterval(() => {
this.refresh()
}, parseInt(this.refreshRate))
}
}
}
}
</script>
<style>
</style>

View File

@ -1,88 +0,0 @@
{
"id": "uni-dateformat",
"displayName": "uni-dateformat 日期格式化",
"version": "1.0.0",
"description": "日期格式化组件可以将日期格式化为1分钟前、刚刚等形式",
"keywords": [
"uni-ui",
"uniui",
"日期格式化",
"时间格式化",
"格式化时间",
""
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,11 +0,0 @@
### DateFormat 日期格式化
> **组件名uni-dateformat**
> 代码块: `uDateformat`
日期格式化组件。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-dateformat)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,25 +0,0 @@
## 1.2.62024-10-12
- 修复 微信小程序中的getSystemInfo警告
## 1.2.52023-03-29
- 新增 pattern.icon 属性,可自定义图标
## 1.2.42022-09-07
小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false)
## 1.2.32022-09-05
- 修复 nvue 环境下,具有 tabBar 时fab 组件下部位置无法正常获取 --window-bottom 的bug详见[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310)
## 1.2.22021-12-29
- 更新 组件依赖
## 1.2.12021-11-19
- 修复 阴影颜色不正确的bug
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab)
## 1.1.12021-11-09
- 新增 提供组件设计资源,组件样式调整
## 1.1.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.72021-05-12
- 新增 组件示例地址
## 1.0.62021-02-05
- 调整为uni_modules目录规范
- 优化 按钮背景色调整
- 优化 兼容pc端

View File

@ -1,491 +0,0 @@
<template>
<view class="uni-cursor-point">
<view v-if="popMenu && (leftBottom||rightBottom||leftTop||rightTop) && content.length > 0" :class="{
'uni-fab--leftBottom': leftBottom,
'uni-fab--rightBottom': rightBottom,
'uni-fab--leftTop': leftTop,
'uni-fab--rightTop': rightTop
}" class="uni-fab"
:style="nvueBottom"
>
<view :class="{
'uni-fab__content--left': horizontal === 'left',
'uni-fab__content--right': horizontal === 'right',
'uni-fab__content--flexDirection': direction === 'vertical',
'uni-fab__content--flexDirectionStart': flexDirectionStart,
'uni-fab__content--flexDirectionEnd': flexDirectionEnd,
'uni-fab__content--other-platform': !isAndroidNvue
}" :style="{ width: boxWidth, height: boxHeight, backgroundColor: styles.backgroundColor }"
class="uni-fab__content" elevation="5">
<view v-if="flexDirectionStart || horizontalLeft" class="uni-fab__item uni-fab__item--first" />
<view v-for="(item, index) in content" :key="index" :class="{ 'uni-fab__item--active': isShow }"
class="uni-fab__item" @click="_onItemClick(index, item)">
<image :src="item.active ? item.selectedIconPath : item.iconPath" class="uni-fab__item-image"
mode="aspectFit" />
<text class="uni-fab__item-text"
:style="{ color: item.active ? styles.selectedColor : styles.color }">{{ item.text }}</text>
</view>
<view v-if="flexDirectionEnd || horizontalRight" class="uni-fab__item uni-fab__item--first" />
</view>
</view>
<view :class="{
'uni-fab__circle--leftBottom': leftBottom,
'uni-fab__circle--rightBottom': rightBottom,
'uni-fab__circle--leftTop': leftTop,
'uni-fab__circle--rightTop': rightTop,
'uni-fab__content--other-platform': !isAndroidNvue
}" class="uni-fab__circle uni-fab__plus" :style="{ 'background-color': styles.buttonColor, 'bottom': nvueBottom }" @click="_onClick">
<uni-icons class="fab-circle-icon" :type="styles.icon" :color="styles.iconColor" size="32"
:class="{'uni-fab__plus--active': isShow && content.length > 0}"></uni-icons>
<!-- <view class="fab-circle-v" :class="{'uni-fab__plus--active': isShow && content.length > 0}"></view>
<view class="fab-circle-h" :class="{'uni-fab__plus--active': isShow && content.length > 0}"></view> -->
</view>
</view>
</template>
<script>
let platform = 'other'
// #ifdef APP-NVUE
platform = uni.getSystemInfoSync().platform
// #endif
/**
* Fab 悬浮按钮
* @description 点击可展开一个图形按钮菜单
* @tutorial https://ext.dcloud.net.cn/plugin?id=144
* @property {Object} pattern 可选样式配置项
* @property {Object} horizontal = [left | right] 水平对齐方式
* @value left 左对齐
* @value right 右对齐
* @property {Object} vertical = [bottom | top] 垂直对齐方式
* @value bottom 下对齐
* @value top 上对齐
* @property {Object} direction = [horizontal | vertical] 展开菜单显示方式
* @value horizontal 水平显示
* @value vertical 垂直显示
* @property {Array} content 展开菜单内容配置项
* @property {Boolean} popMenu 是否使用弹出菜单
* @event {Function} trigger 展开菜单点击事件返回点击信息
* @event {Function} fabClick 悬浮按钮点击事件
*/
export default {
name: 'UniFab',
emits: ['fabClick', 'trigger'],
props: {
pattern: {
type: Object,
default () {
return {}
}
},
horizontal: {
type: String,
default: 'left'
},
vertical: {
type: String,
default: 'bottom'
},
direction: {
type: String,
default: 'horizontal'
},
content: {
type: Array,
default () {
return []
}
},
show: {
type: Boolean,
default: false
},
popMenu: {
type: Boolean,
default: true
}
},
data() {
return {
fabShow: false,
isShow: false,
isAndroidNvue: platform === 'android',
styles: {
color: '#3c3e49',
selectedColor: '#007AFF',
backgroundColor: '#fff',
buttonColor: '#007AFF',
iconColor: '#fff',
icon: 'plusempty'
}
}
},
computed: {
contentWidth(e) {
return (this.content.length + 1) * 55 + 15 + 'px'
},
contentWidthMin() {
return '55px'
},
//
boxWidth() {
return this.getPosition(3, 'horizontal')
},
//
boxHeight() {
return this.getPosition(3, 'vertical')
},
//
leftBottom() {
return this.getPosition(0, 'left', 'bottom')
},
//
rightBottom() {
return this.getPosition(0, 'right', 'bottom')
},
//
leftTop() {
return this.getPosition(0, 'left', 'top')
},
rightTop() {
return this.getPosition(0, 'right', 'top')
},
flexDirectionStart() {
return this.getPosition(1, 'vertical', 'top')
},
flexDirectionEnd() {
return this.getPosition(1, 'vertical', 'bottom')
},
horizontalLeft() {
return this.getPosition(2, 'horizontal', 'left')
},
horizontalRight() {
return this.getPosition(2, 'horizontal', 'right')
},
// nvue bottom
nvueBottom() {
// #ifdef APP-NVUE
const safeBottom = uni.getSystemInfoSync().windowBottom;
return 30 + safeBottom
// #endif
// #ifndef APP-NVUE
return 30
// #endif
}
},
watch: {
pattern: {
handler(val, oldVal) {
this.styles = Object.assign({}, this.styles, val)
},
deep: true
}
},
created() {
this.isShow = this.show
if (this.top === 0) {
this.fabShow = true
}
//
this.styles = Object.assign({}, this.styles, this.pattern)
},
methods: {
_onClick() {
this.$emit('fabClick')
if (!this.popMenu) {
return
}
this.isShow = !this.isShow
},
open() {
this.isShow = true
},
close() {
this.isShow = false
},
/**
* 按钮点击事件
*/
_onItemClick(index, item) {
if (!this.isShow) {
return
}
this.$emit('trigger', {
index,
item
})
},
/**
* 获取 位置信息
*/
getPosition(types, paramA, paramB) {
if (types === 0) {
return this.horizontal === paramA && this.vertical === paramB
} else if (types === 1) {
return this.direction === paramA && this.vertical === paramB
} else if (types === 2) {
return this.direction === paramA && this.horizontal === paramB
} else {
return this.isShow && this.direction === paramA ? this.contentWidth : this.contentWidthMin
}
}
}
}
</script>
<style lang="scss" >
$uni-shadow-base:0 1px 5px 2px rgba($color: #000000, $alpha: 0.3) !default;
.uni-fab {
position: fixed;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
z-index: 10;
border-radius: 45px;
box-shadow: $uni-shadow-base;
}
.uni-cursor-point {
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-fab--active {
opacity: 1;
}
.uni-fab--leftBottom {
left: 15px;
bottom: 30px;
/* #ifdef H5 */
left: calc(15px + var(--window-left));
bottom: calc(30px + var(--window-bottom));
/* #endif */
// padding: 10px;
}
.uni-fab--leftTop {
left: 15px;
top: 30px;
/* #ifdef H5 */
left: calc(15px + var(--window-left));
top: calc(30px + var(--window-top));
/* #endif */
// padding: 10px;
}
.uni-fab--rightBottom {
right: 15px;
bottom: 30px;
/* #ifdef H5 */
right: calc(15px + var(--window-right));
bottom: calc(30px + var(--window-bottom));
/* #endif */
// padding: 10px;
}
.uni-fab--rightTop {
right: 15px;
top: 30px;
/* #ifdef H5 */
right: calc(15px + var(--window-right));
top: calc(30px + var(--window-top));
/* #endif */
// padding: 10px;
}
.uni-fab__circle {
position: fixed;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
width: 55px;
height: 55px;
background-color: #3c3e49;
border-radius: 45px;
z-index: 11;
// box-shadow: $uni-shadow-base;
}
.uni-fab__circle--leftBottom {
left: 15px;
bottom: 30px;
/* #ifdef H5 */
left: calc(15px + var(--window-left));
bottom: calc(30px + var(--window-bottom));
/* #endif */
}
.uni-fab__circle--leftTop {
left: 15px;
top: 30px;
/* #ifdef H5 */
left: calc(15px + var(--window-left));
top: calc(30px + var(--window-top));
/* #endif */
}
.uni-fab__circle--rightBottom {
right: 15px;
bottom: 30px;
/* #ifdef H5 */
right: calc(15px + var(--window-right));
bottom: calc(30px + var(--window-bottom));
/* #endif */
}
.uni-fab__circle--rightTop {
right: 15px;
top: 30px;
/* #ifdef H5 */
right: calc(15px + var(--window-right));
top: calc(30px + var(--window-top));
/* #endif */
}
.uni-fab__circle--left {
left: 0;
}
.uni-fab__circle--right {
right: 0;
}
.uni-fab__circle--top {
top: 0;
}
.uni-fab__circle--bottom {
bottom: 0;
}
.uni-fab__plus {
font-weight: bold;
}
// .fab-circle-v {
// position: absolute;
// width: 2px;
// height: 24px;
// left: 0;
// top: 0;
// right: 0;
// bottom: 0;
// /* #ifndef APP-NVUE */
// margin: auto;
// /* #endif */
// background-color: white;
// transform: rotate(0deg);
// transition: transform 0.3s;
// }
// .fab-circle-h {
// position: absolute;
// width: 24px;
// height: 2px;
// left: 0;
// top: 0;
// right: 0;
// bottom: 0;
// /* #ifndef APP-NVUE */
// margin: auto;
// /* #endif */
// background-color: white;
// transform: rotate(0deg);
// transition: transform 0.3s;
// }
.fab-circle-icon {
transform: rotate(0deg);
transition: transform 0.3s;
font-weight: 200;
}
.uni-fab__plus--active {
transform: rotate(135deg);
}
.uni-fab__content {
/* #ifndef APP-NVUE */
box-sizing: border-box;
display: flex;
/* #endif */
flex-direction: row;
border-radius: 55px;
overflow: hidden;
transition-property: width, height;
transition-duration: 0.2s;
width: 55px;
border-color: #DDDDDD;
border-width: 1rpx;
border-style: solid;
}
.uni-fab__content--other-platform {
border-width: 0px;
box-shadow: $uni-shadow-base;
}
.uni-fab__content--left {
justify-content: flex-start;
}
.uni-fab__content--right {
justify-content: flex-end;
}
.uni-fab__content--flexDirection {
flex-direction: column;
justify-content: flex-end;
}
.uni-fab__content--flexDirectionStart {
flex-direction: column;
justify-content: flex-start;
}
.uni-fab__content--flexDirectionEnd {
flex-direction: column;
justify-content: flex-end;
}
.uni-fab__item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 55px;
height: 55px;
opacity: 0;
transition: opacity 0.2s;
}
.uni-fab__item--active {
opacity: 1;
}
.uni-fab__item-image {
width: 20px;
height: 20px;
margin-bottom: 4px;
}
.uni-fab__item-text {
color: #FFFFFF;
font-size: 12px;
line-height: 12px;
margin-top: 2px;
}
.uni-fab__item--first {
width: 55px;
}
</style>

View File

@ -1,85 +0,0 @@
{
"id": "uni-fab",
"displayName": "uni-fab 悬浮按钮",
"version": "1.2.6",
"description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。",
"keywords": [
"uni-ui",
"uniui",
"按钮",
"悬浮按钮",
"fab"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"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"
},
"uni_modules": {
"dependencies": ["uni-scss","uni-icons"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,9 +0,0 @@
## Fab 悬浮按钮
> **组件名uni-fab**
> 代码块: `uFab`
点击可展开一个图形按钮菜单
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fab)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,19 +0,0 @@
## 1.2.12022-05-30
- 新增 stat 属性 是否开启uni统计功能
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fav](https://uniapp.dcloud.io/component/uniui/uni-fav)
## 1.1.12021-08-24
- 新增 支持国际化
## 1.1.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.62021-05-12
- 新增 组件示例地址
## 1.0.52021-04-21
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
## 1.0.42021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.0.32021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.0.22021-02-05
- 调整为uni_modules目录规范

View File

@ -1,4 +0,0 @@
{
"uni-fav.collect": "collect",
"uni-fav.collected": "collected"
}

View File

@ -1,8 +0,0 @@
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
}

View File

@ -1,4 +0,0 @@
{
"uni-fav.collect": "收藏",
"uni-fav.collected": "已收藏"
}

View File

@ -1,4 +0,0 @@
{
"uni-fav.collect": "收藏",
"uni-fav.collected": "已收藏"
}

View File

@ -1,161 +0,0 @@
<template>
<view :class="[circle === true || circle === 'true' ? 'uni-fav--circle' : '']" :style="[{ backgroundColor: checked ? bgColorChecked : bgColor }]"
@click="onClick" class="uni-fav">
<!-- #ifdef MP-ALIPAY -->
<view class="uni-fav-star" v-if="!checked && (star === true || star === 'true')">
<uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" size="14" type="star-filled" />
</view>
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-star" size="14" type="star-filled"
v-if="!checked && (star === true || star === 'true')" />
<!-- #endif -->
<text :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-text">{{ checked ? contentFav : contentDefault }}</text>
</view>
</template>
<script>
/**
* Fav 收藏按钮
* @description 用于收藏功能可点击切换选中不选中的状态
* @tutorial https://ext.dcloud.net.cn/plugin?id=864
* @property {Boolean} star = [true|false] 按钮是否带星星
* @property {String} bgColor 未收藏时的背景色
* @property {String} bgColorChecked 已收藏时的背景色
* @property {String} fgColor 未收藏时的文字颜色
* @property {String} fgColorChecked 已收藏时的文字颜色
* @property {Boolean} circle = [true|false] 是否为圆角
* @property {Boolean} checked = [true|false] 是否为已收藏
* @property {Object} contentText = [true|false] 收藏按钮文字
* @property {Boolean} stat 是否开启统计功能
* @event {Function} click 点击 fav按钮触发事件
* @example <uni-fav :checked="true"/>
*/
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const { t } = initVueI18n(messages)
export default {
name: "UniFav",
// TODO vue3
emits: ['click'],
props: {
star: {
type: [Boolean, String],
default: true
},
bgColor: {
type: String,
default: "#eeeeee"
},
fgColor: {
type: String,
default: "#666666"
},
bgColorChecked: {
type: String,
default: "#007aff"
},
fgColorChecked: {
type: String,
default: "#FFFFFF"
},
circle: {
type: [Boolean, String],
default: false
},
checked: {
type: Boolean,
default: false
},
contentText: {
type: Object,
default () {
return {
contentDefault: "",
contentFav: ""
};
}
},
stat:{
type: Boolean,
default: false
}
},
computed: {
contentDefault() {
return this.contentText.contentDefault || t("uni-fav.collect")
},
contentFav() {
return this.contentText.contentFav || t("uni-fav.collected")
},
},
watch: {
checked() {
if (uni.report && this.stat) {
if (this.checked) {
uni.report("收藏", "收藏");
} else {
uni.report("取消收藏", "取消收藏");
}
}
}
},
methods: {
onClick() {
this.$emit("click");
}
}
};
</script>
<style lang="scss" >
$fav-height: 25px;
.uni-fav {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
width: 60px;
height: $fav-height;
line-height: $fav-height;
text-align: center;
border-radius: 3px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-fav--circle {
border-radius: 30px;
}
.uni-fav-star {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
height: $fav-height;
line-height: 24px;
margin-right: 3px;
align-items: center;
justify-content: center;
}
.uni-fav-text {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
height: $fav-height;
line-height: $fav-height;
align-items: center;
justify-content: center;
font-size: 12px;
}
</style>

View File

@ -1,89 +0,0 @@
{
"id": "uni-fav",
"displayName": "uni-fav 收藏按钮",
"version": "1.2.1",
"description": " Fav 收藏组件,可自定义颜色、大小。",
"keywords": [
"fav",
"uni-ui",
"uniui",
"收藏"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,10 +0,0 @@
## Fav 收藏按钮
> **组件名uni-fav**
> 代码块: `uFav`
用于收藏功能,可点击切换选中、不选中的状态。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fav)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,81 +0,0 @@
## 1.0.112024-07-19
- 修复 vue3 使用value报错的bug
## 1.0.102024-07-09
- 优化 vue3兼容性
## 1.0.92024-07-09
- 修复 value 属性不兼容vue3的bug
## 1.0.82024-03-20
- 补充 删除文件时返回文件下标
## 1.0.72024-02-21
- 新增 微信小程序选择视频时改用chooseMedia,并返回视频缩略图
## 1.0.62024-01-06
- 新增 微信小程序不再调用chooseImage,而是调用chooseMedia
## 1.0.52024-01-03
- 新增 上传文件至云存储携带本地文件名称
## 1.0.42023-03-29
- 修复 手动上传删除一个文件后不能再上传的bug
## 1.0.32022-12-19
- 新增 sourceType 属性, 可以自定义图片和视频选择的来源
## 1.0.22022-07-04
- 修复 在uni-forms下样式不生效的bug
## 1.0.12021-11-23
- 修复 参数为对象的情况下url在某些情况显示错误的bug
## 1.0.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-file-picker](https://uniapp.dcloud.io/component/uniui/uni-file-picker)
## 0.2.162021-11-08
- 修复 传入空对象 显示错误的Bug
## 0.2.152021-08-30
- 修复 return-type="object" 时且存在v-model时无法删除文件的Bug
## 0.2.142021-08-23
- 新增 参数中返回 fileID 字段
## 0.2.132021-08-23
- 修复 腾讯云传入fileID 不能回显的bug
- 修复 选择图片后,不能放大的问题
## 0.2.122021-08-17
- 修复 由于 0.2.11 版本引起的不能回显图片的Bug
## 0.2.112021-08-16
- 新增 clearFiles(index) 方法,可以手动删除指定文件
- 修复 v-model 值设为 null 报错的Bug
## 0.2.102021-08-13
- 修复 return-type="object" 时无法删除文件的Bug
## 0.2.92021-08-03
- 修复 auto-upload 属性失效的Bug
## 0.2.82021-07-31
- 修复 fileExtname属性不指定值报错的Bug
## 0.2.72021-07-31
- 修复 在某种场景下图片不回显的Bug
## 0.2.62021-07-30
- 修复 return-type为object下返回值不正确的Bug
## 0.2.52021-07-30
- 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题
## 0.2.32021-07-28
- 优化 调整示例代码
## 0.2.22021-07-27
- 修复 vue3 下赋值错误的Bug
- 优化 h5平台下上传文件导致页面卡死的问题
## 0.2.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.1.12021-07-02
- 修复 sourceType 缺少默认值导致 ios 无法选择文件
## 0.1.02021-06-30
- 优化 解耦与uniCloud的强绑定关系 如不绑定服务空间默认autoUpload为false且不可更改
## 0.0.112021-06-30
- 修复 由 0.0.10 版本引发的 returnType 属性失效的问题
## 0.0.102021-06-29
- 优化 文件上传后进度条消失时机
## 0.0.92021-06-29
- 修复 在uni-forms 中,删除文件 获取的值不对的Bug
## 0.0.82021-06-15
- 修复 删除文件时无法触发 v-model 的Bug
## 0.0.72021-05-12
- 新增 组件示例地址
## 0.0.62021-04-09
- 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug
## 0.0.52021-04-09
- 优化 更新组件示例
## 0.0.42021-04-09
- 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔
## 0.0.32021-02-05
- 调整为uni_modules目录规范
- 修复 微信小程序不指定 fileExtname 属性选择失败的Bug

View File

@ -1,287 +0,0 @@
'use strict';
const ERR_MSG_OK = 'chooseAndUploadFile:ok';
const ERR_MSG_FAIL = 'chooseAndUploadFile:fail';
function chooseImage(opts) {
const {
count,
sizeType = ['original', 'compressed'],
sourceType,
extension
} = opts
return new Promise((resolve, reject) => {
// 微信由于旧接口不再维护针对微信小程序平台改用chooseMedia接口
// #ifdef MP-WEIXIN
uni.chooseMedia({
count,
sizeType,
sourceType,
mediaType: ['image'],
extension,
success(res) {
res.tempFiles.forEach(item => {
item.path = item.tempFilePath;
})
resolve(normalizeChooseAndUploadFileRes(res, 'image'));
},
fail(res) {
reject({
errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
});
},
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseImage({
count,
sizeType,
sourceType,
extension,
success(res) {
resolve(normalizeChooseAndUploadFileRes(res, 'image'));
},
fail(res) {
reject({
errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
});
},
});
// #endif
});
}
function chooseVideo(opts) {
const {
count,
camera,
compressed,
maxDuration,
sourceType,
extension
} = opts;
return new Promise((resolve, reject) => {
// 微信由于旧接口不再维护针对微信小程序平台改用chooseMedia接口
// #ifdef MP-WEIXIN
uni.chooseMedia({
count,
compressed,
maxDuration,
sourceType,
extension,
mediaType: ['video'],
success(res) {
const {
tempFiles,
} = res;
resolve(normalizeChooseAndUploadFileRes({
errMsg: 'chooseVideo:ok',
tempFiles: tempFiles.map(item => {
return {
name: item.name || '',
path: item.tempFilePath,
thumbTempFilePath: item.thumbTempFilePath,
size:item.size,
type: (res.tempFile && res.tempFile.type) || '',
width:item.width,
height:item.height,
duration:item.duration,
fileType: 'video',
cloudPath: '',
}
}),
}, 'video'));
},
fail(res) {
reject({
errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL),
});
},
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseVideo({
camera,
compressed,
maxDuration,
sourceType,
extension,
success(res) {
const {
tempFilePath,
duration,
size,
height,
width
} = res;
resolve(normalizeChooseAndUploadFileRes({
errMsg: 'chooseVideo:ok',
tempFilePaths: [tempFilePath],
tempFiles: [{
name: (res.tempFile && res.tempFile.name) || '',
path: tempFilePath,
size,
type: (res.tempFile && res.tempFile.type) || '',
width,
height,
duration,
fileType: 'video',
cloudPath: '',
}, ],
}, 'video'));
},
fail(res) {
reject({
errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL),
});
},
});
// #endif
});
}
function chooseAll(opts) {
const {
count,
extension
} = opts;
return new Promise((resolve, reject) => {
let chooseFile = uni.chooseFile;
if (typeof wx !== 'undefined' &&
typeof wx.chooseMessageFile === 'function') {
chooseFile = wx.chooseMessageFile;
}
if (typeof chooseFile !== 'function') {
return reject({
errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。',
});
}
chooseFile({
type: 'all',
count,
extension,
success(res) {
resolve(normalizeChooseAndUploadFileRes(res));
},
fail(res) {
reject({
errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL),
});
},
});
});
}
function normalizeChooseAndUploadFileRes(res, fileType) {
res.tempFiles.forEach((item, index) => {
if (!item.name) {
item.name = item.path.substring(item.path.lastIndexOf('/') + 1);
}
if (fileType) {
item.fileType = fileType;
}
item.cloudPath =
Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.'));
});
if (!res.tempFilePaths) {
res.tempFilePaths = res.tempFiles.map((file) => file.path);
}
return res;
}
function uploadCloudFiles(files, max = 5, onUploadProgress) {
files = JSON.parse(JSON.stringify(files))
const len = files.length
let count = 0
let self = this
return new Promise(resolve => {
while (count < max) {
next()
}
function next() {
let cur = count++
if (cur >= len) {
!files.find(item => !item.url && !item.errMsg) && resolve(files)
return
}
const fileItem = files[cur]
const index = self.files.findIndex(v => v.uuid === fileItem.uuid)
fileItem.url = ''
delete fileItem.errMsg
uniCloud
.uploadFile({
filePath: fileItem.path,
cloudPath: fileItem.cloudPath,
fileType: fileItem.fileType,
onUploadProgress: res => {
res.index = index
onUploadProgress && onUploadProgress(res)
}
})
.then(res => {
fileItem.url = res.fileID
fileItem.index = index
if (cur < len) {
next()
}
})
.catch(res => {
fileItem.errMsg = res.errMsg || res.message
fileItem.index = index
if (cur < len) {
next()
}
})
}
})
}
function uploadFiles(choosePromise, {
onChooseFile,
onUploadProgress
}) {
return choosePromise
.then((res) => {
if (onChooseFile) {
const customChooseRes = onChooseFile(res);
if (typeof customChooseRes !== 'undefined') {
return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ?
res : chooseRes);
}
}
return res;
})
.then((res) => {
if (res === false) {
return {
errMsg: ERR_MSG_OK,
tempFilePaths: [],
tempFiles: [],
};
}
return res
})
}
function chooseAndUploadFile(opts = {
type: 'all'
}) {
if (opts.type === 'image') {
return uploadFiles(chooseImage(opts), opts);
} else if (opts.type === 'video') {
return uploadFiles(chooseVideo(opts), opts);
}
return uploadFiles(chooseAll(opts), opts);
}
export {
chooseAndUploadFile,
uploadCloudFiles
};

View File

@ -1,668 +0,0 @@
<template>
<view class="uni-file-picker">
<view v-if="title" class="uni-file-picker__header">
<text class="file-title">{{ title }}</text>
<text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
</view>
<upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly"
:image-styles="imageStyles" :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview"
:delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
<slot>
<view class="is-add">
<view class="icon-add"></view>
<view class="icon-add rotate"></view>
</view>
</slot>
</upload-image>
<upload-file v-if="fileMediatype !== 'image' || showType !== 'grid'" :readonly="readonly"
:list-styles="listStyles" :files-list="filesList" :showType="showType" :delIcon="delIcon"
@uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
<slot><button type="primary" size="mini">选择文件</button></slot>
</upload-file>
</view>
</template>
<script>
import {
chooseAndUploadFile,
uploadCloudFiles
} from './choose-and-upload-file.js'
import {
get_file_ext,
get_extname,
get_files_and_is_max,
get_file_info,
get_file_data
} from './utils.js'
import uploadImage from './upload-image.vue'
import uploadFile from './upload-file.vue'
let fileInput = null
/**
* FilePicker 文件选择上传
* @description 文件选择上传组件可以选择图片视频等任意文件并上传到当前绑定的服务空间
* @tutorial https://ext.dcloud.net.cn/plugin?id=4079
* @property {Object|Array} value 组件数据通常用来回显 ,类型由return-type属性决定
* @property {Boolean} disabled = [true|false] 组件禁用
* @value true 禁用
* @value false 取消禁用
* @property {Boolean} readonly = [true|false] 组件只读不可选择不显示进度不显示删除按钮
* @value true 只读
* @value false 取消只读
* @property {String} return-type = [array|object] 限制 value 格式当为 object 组件只能单选且会覆盖
* @value array 规定 value 属性的类型为数组
* @value object 规定 value 属性的类型为对象
* @property {Boolean} disable-preview = [true|false] 禁用图片预览 mode:grid 时生效
* @value true 禁用图片预览
* @value false 取消禁用图片预览
* @property {Boolean} del-icon = [true|false] 是否显示删除按钮
* @value true 显示删除按钮
* @value false 不显示删除按钮
* @property {Boolean} auto-upload = [true|false] 是否自动上传值为true则只触发@select,可自行上传
* @value true 自动上传
* @value false 取消自动上传
* @property {Number|String} limit 最大选择个数 h5 会自动忽略多选的部分
* @property {String} title 组件标题右侧显示上传计数
* @property {String} mode = [list|grid] 选择文件后的文件列表样式
* @value list 列表显示
* @value grid 宫格显示
* @property {String} file-mediatype = [image|video|all] 选择文件类型
* @value image 只选择图片
* @value video 只选择视频
* @value all 选择所有文件
* @property {Array} file-extname 选择文件后缀根据 file-mediatype 属性而不同
* @property {Object} list-style mode:list 时的样式
* @property {Object} image-styles 选择文件后缀根据 file-mediatype 属性而不同
* @event {Function} select 选择文件后触发
* @event {Function} progress 文件上传时触发
* @event {Function} success 上传成功触发
* @event {Function} fail 上传失败触发
* @event {Function} delete 文件从列表移除时触发
*/
export default {
name: 'uniFilePicker',
components: {
uploadImage,
uploadFile
},
options: {
virtualHost: true
},
emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'],
props: {
modelValue: {
type: [Array, Object],
default () {
return []
}
},
value: {
type: [Array, Object],
default () {
return []
}
},
disabled: {
type: Boolean,
default: false
},
disablePreview: {
type: Boolean,
default: false
},
delIcon: {
type: Boolean,
default: true
},
//
autoUpload: {
type: Boolean,
default: true
},
// h5
limit: {
type: [Number, String],
default: 9
},
// grid | list | list-card
mode: {
type: String,
default: 'grid'
},
// image/video/all
fileMediatype: {
type: String,
default: 'image'
},
//
fileExtname: {
type: [Array, String],
default () {
return []
}
},
title: {
type: String,
default: ''
},
listStyles: {
type: Object,
default () {
return {
//
border: true,
// 线
dividline: true,
// 线
borderStyle: {}
}
}
},
imageStyles: {
type: Object,
default () {
return {
width: 'auto',
height: 'auto'
}
}
},
readonly: {
type: Boolean,
default: false
},
returnType: {
type: String,
default: 'array'
},
sizeType: {
type: Array,
default () {
return ['original', 'compressed']
}
},
sourceType: {
type: Array,
default () {
return ['album', 'camera']
}
},
provider: {
type: String,
default: '' // unicloud extStorage
}
},
data() {
return {
files: [],
localValue: []
}
},
watch: {
value: {
handler(newVal, oldVal) {
this.setValue(newVal, oldVal)
},
immediate: true
},
modelValue: {
handler(newVal, oldVal) {
this.setValue(newVal, oldVal)
},
immediate: true
},
},
computed: {
filesList() {
let files = []
this.files.forEach(v => {
files.push(v)
})
return files
},
showType() {
if (this.fileMediatype === 'image') {
return this.mode
}
return 'list'
},
limitLength() {
if (this.returnType === 'object') {
return 1
}
if (!this.limit) {
return 1
}
if (this.limit >= 9) {
return 9
}
return this.limit
}
},
created() {
// TODO
if (!(uniCloud.config && uniCloud.config.provider)) {
this.noSpace = true
uniCloud.chooseAndUploadFile = chooseAndUploadFile
}
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
if (this.form && this.formItem) {
if (this.formItem.name) {
this.rename = this.formItem.name
this.form.inputChildrens.push(this)
}
}
},
methods: {
/**
* 公开用户使用清空文件
* @param {Object} index
*/
clearFiles(index) {
if (index !== 0 && !index) {
this.files = []
this.$nextTick(() => {
this.setEmit()
})
} else {
this.files.splice(index, 1)
}
this.$nextTick(() => {
this.setEmit()
})
},
/**
* 公开用户使用继续上传
*/
upload() {
let files = []
this.files.forEach((v, index) => {
if (v.status === 'ready' || v.status === 'error') {
files.push(Object.assign({}, v))
}
})
return this.uploadFiles(files)
},
async setValue(newVal, oldVal) {
const newData = async (v) => {
const reg = /cloud:\/\/([\w.]+\/?)\S*/
let url = ''
if(v.fileID){
url = v.fileID
}else{
url = v.url
}
if (reg.test(url)) {
v.fileID = url
v.url = await this.getTempFileURL(url)
}
if(v.url) v.path = v.url
return v
}
if (this.returnType === 'object') {
if (newVal) {
await newData(newVal)
} else {
newVal = {}
}
} else {
if (!newVal) newVal = []
for(let i =0 ;i < newVal.length ;i++){
let v = newVal[i]
await newData(v)
}
}
this.localValue = newVal
if (this.form && this.formItem &&!this.is_reset) {
this.is_reset = false
this.formItem.setValue(this.localValue)
}
let filesData = Object.keys(newVal).length > 0 ? newVal : [];
this.files = [].concat(filesData)
},
/**
* 选择文件
*/
choose() {
if (this.disabled) return
if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType ===
'array') {
uni.showToast({
title: `您最多选择 ${this.limitLength} 个文件`,
icon: 'none'
})
return
}
this.chooseFiles()
},
/**
* 选择文件并上传
*/
chooseFiles() {
const _extname = get_extname(this.fileExtname)
//
uniCloud
.chooseAndUploadFile({
type: this.fileMediatype,
compressed: false,
sizeType: this.sizeType,
sourceType: this.sourceType,
// TODO video
extension: _extname.length > 0 ? _extname : undefined,
count: this.limitLength - this.files.length, //9
onChooseFile: this.chooseFileCallback,
onUploadProgress: progressEvent => {
this.setProgress(progressEvent, progressEvent.index)
}
})
.then(result => {
this.setSuccessAndError(result.tempFiles)
})
.catch(err => {
console.log('选择失败', err)
})
},
/**
* 选择文件回调
* @param {Object} res
*/
async chooseFileCallback(res) {
const _extname = get_extname(this.fileExtname)
const is_one = (Number(this.limitLength) === 1 &&
this.disablePreview &&
!this.disabled) ||
this.returnType === 'object'
//
if (is_one) {
this.files = []
}
let {
filePaths,
files
} = get_files_and_is_max(res, _extname)
if (!(_extname && _extname.length > 0)) {
filePaths = res.tempFilePaths
files = res.tempFiles
}
let currentData = []
for (let i = 0; i < files.length; i++) {
if (this.limitLength - this.files.length <= 0) break
files[i].uuid = Date.now()
let filedata = await get_file_data(files[i], this.fileMediatype)
filedata.progress = 0
filedata.status = 'ready'
this.files.push(filedata)
currentData.push({
...filedata,
file: files[i]
})
}
this.$emit('select', {
tempFiles: currentData,
tempFilePaths: filePaths
})
res.tempFiles = files
//
if (!this.autoUpload || this.noSpace) {
res.tempFiles = []
}
res.tempFiles.forEach((fileItem, index) => {
this.provider && (fileItem.provider = this.provider);
const fileNameSplit = fileItem.name.split('.')
const ext = fileNameSplit.pop()
const fileName = fileNameSplit.join('.').replace(/[\s\/\?<>\\:\*\|":]/g, '_')
fileItem.cloudPath = fileName + '_' + Date.now() + '_' + index + '.' + ext
})
},
/**
* 批传
* @param {Object} e
*/
uploadFiles(files) {
files = [].concat(files)
return uploadCloudFiles.call(this, files, 5, res => {
this.setProgress(res, res.index, true)
})
.then(result => {
this.setSuccessAndError(result)
return result;
})
.catch(err => {
console.log(err)
})
},
/**
* 成功或失败
*/
async setSuccessAndError(res, fn) {
let successData = []
let errorData = []
let tempFilePath = []
let errorTempFilePath = []
for (let i = 0; i < res.length; i++) {
const item = res[i]
const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index
if (index === -1 || !this.files) break
if (item.errMsg === 'request:fail') {
this.files[index].url = item.path
this.files[index].status = 'error'
this.files[index].errMsg = item.errMsg
// this.files[index].progress = -1
errorData.push(this.files[index])
errorTempFilePath.push(this.files[index].url)
} else {
this.files[index].errMsg = ''
this.files[index].fileID = item.url
const reg = /cloud:\/\/([\w.]+\/?)\S*/
if (reg.test(item.url)) {
this.files[index].url = await this.getTempFileURL(item.url)
}else{
this.files[index].url = item.url
}
this.files[index].status = 'success'
this.files[index].progress += 1
successData.push(this.files[index])
tempFilePath.push(this.files[index].fileID)
}
}
if (successData.length > 0) {
this.setEmit()
//
this.$emit('success', {
tempFiles: this.backObject(successData),
tempFilePaths: tempFilePath
})
}
if (errorData.length > 0) {
this.$emit('fail', {
tempFiles: this.backObject(errorData),
tempFilePaths: errorTempFilePath
})
}
},
/**
* 获取进度
* @param {Object} progressEvent
* @param {Object} index
* @param {Object} type
*/
setProgress(progressEvent, index, type) {
const fileLenth = this.files.length
const percentNum = (index / fileLenth) * 100
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
let idx = index
if (!type) {
idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid)
}
if (idx === -1 || !this.files[idx]) return
// fix by mehaotian 100 -1
this.files[idx].progress = percentCompleted - 1
//
this.$emit('progress', {
index: idx,
progress: parseInt(percentCompleted),
tempFile: this.files[idx]
})
},
/**
* 删除文件
* @param {Object} index
*/
delFile(index) {
this.$emit('delete', {
index,
tempFile: this.files[index],
tempFilePath: this.files[index].url
})
this.files.splice(index, 1)
this.$nextTick(() => {
this.setEmit()
})
},
/**
* 获取文件名和后缀
* @param {Object} name
*/
getFileExt(name) {
const last_len = name.lastIndexOf('.')
const len = name.length
return {
name: name.substring(0, last_len),
ext: name.substring(last_len + 1, len)
}
},
/**
* 处理返回事件
*/
setEmit() {
let data = []
if (this.returnType === 'object') {
data = this.backObject(this.files)[0]
this.localValue = data?data:null
} else {
data = this.backObject(this.files)
if (!this.localValue) {
this.localValue = []
}
this.localValue = [...data]
}
// #ifdef VUE3
this.$emit('update:modelValue', this.localValue)
// #endif
// #ifndef VUE3
this.$emit('input', this.localValue)
// #endif
},
/**
* 处理返回参数
* @param {Object} files
*/
backObject(files) {
let newFilesData = []
files.forEach(v => {
newFilesData.push({
extname: v.extname,
fileType: v.fileType,
image: v.image,
name: v.name,
path: v.path,
size: v.size,
fileID:v.fileID,
url: v.url,
// bug, #694
uuid: v.uuid,
status: v.status,
cloudPath: v.cloudPath
})
})
return newFilesData
},
async getTempFileURL(fileList) {
fileList = {
fileList: [].concat(fileList)
}
const urls = await uniCloud.getTempFileURL(fileList)
return urls.fileList[0].tempFileURL || ''
},
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
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;
}
}
}
</script>
<style>
.uni-file-picker {
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
width: 100%;
/* #endif */
flex: 1;
}
.uni-file-picker__header {
padding-top: 5px;
padding-bottom: 10px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: space-between;
}
.file-title {
font-size: 14px;
color: #333;
}
.file-count {
font-size: 14px;
color: #999;
}
.is-add {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.icon-add {
width: 50px;
height: 5px;
background-color: #f1f1f1;
border-radius: 2px;
}
.rotate {
position: absolute;
transform: rotate(90deg);
}
</style>

View File

@ -1,325 +0,0 @@
<template>
<view class="uni-file-picker__files">
<view v-if="!readonly" class="files-button" @click="choose">
<slot></slot>
</view>
<!-- :class="{'is-text-box':showType === 'list'}" -->
<view v-if="list.length > 0" class="uni-file-picker__lists is-text-box" :style="borderStyle">
<!-- ,'is-list-card':showType === 'list-card' -->
<view class="uni-file-picker__lists-box" v-for="(item ,index) in list" :key="index" :class="{
'files-border':index !== 0 && styles.dividline}"
:style="index !== 0 && styles.dividline &&borderLineStyle">
<view class="uni-file-picker__item">
<!-- :class="{'is-text-image':showType === 'list'}" -->
<!-- <view class="files__image is-text-image">
<image class="header-image" :src="item.logo" mode="aspectFit"></image>
</view> -->
<view class="files__name">{{item.name}}</view>
<view v-if="delIcon&&!readonly" class="icon-del-box icon-files" @click="delFile(index)">
<view class="icon-del icon-files"></view>
<view class="icon-del rotate"></view>
</view>
</view>
<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
:backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
</view>
<view v-if="item.status === 'error'" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
点击重试
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: "uploadFile",
emits:['uploadFiles','choose','delFile'],
props: {
filesList: {
type: Array,
default () {
return []
}
},
delIcon: {
type: Boolean,
default: true
},
limit: {
type: [Number, String],
default: 9
},
showType: {
type: String,
default: ''
},
listStyles: {
type: Object,
default () {
return {
//
border: true,
// 线
dividline: true,
// 线
borderStyle: {}
}
}
},
readonly:{
type:Boolean,
default:false
}
},
computed: {
list() {
let files = []
this.filesList.forEach(v => {
files.push(v)
})
return files
},
styles() {
let styles = {
border: true,
dividline: true,
'border-style': {}
}
return Object.assign(styles, this.listStyles)
},
borderStyle() {
let {
borderStyle,
border
} = this.styles
let obj = {}
if (!border) {
obj.border = 'none'
} else {
let width = (borderStyle && borderStyle.width) || 1
width = this.value2px(width)
let radius = (borderStyle && borderStyle.radius) || 5
radius = this.value2px(radius)
obj = {
'border-width': width,
'border-style': (borderStyle && borderStyle.style) || 'solid',
'border-color': (borderStyle && borderStyle.color) || '#eee',
'border-radius': radius
}
}
let classles = ''
for (let i in obj) {
classles += `${i}:${obj[i]};`
}
return classles
},
borderLineStyle() {
let obj = {}
let {
borderStyle
} = this.styles
if (borderStyle && borderStyle.color) {
obj['border-color'] = borderStyle.color
}
if (borderStyle && borderStyle.width) {
let width = borderStyle && borderStyle.width || 1
let style = borderStyle && borderStyle.style || 0
if (typeof width === 'number') {
width += 'px'
} else {
width = width.indexOf('px') ? width : width + 'px'
}
obj['border-width'] = width
if (typeof style === 'number') {
style += 'px'
} else {
style = style.indexOf('px') ? style : style + 'px'
}
obj['border-top-style'] = style
}
let classles = ''
for (let i in obj) {
classles += `${i}:${obj[i]};`
}
return classles
}
},
methods: {
uploadFiles(item, index) {
this.$emit("uploadFiles", {
item,
index
})
},
choose() {
this.$emit("choose")
},
delFile(index) {
this.$emit('delFile', index)
},
value2px(value) {
if (typeof value === 'number') {
value += 'px'
} else {
value = value.indexOf('px') !== -1 ? value : value + 'px'
}
return value
}
}
}
</script>
<style lang="scss">
.uni-file-picker__files {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: flex-start;
}
.files-button {
// border: 1px red solid;
}
.uni-file-picker__lists {
position: relative;
margin-top: 5px;
overflow: hidden;
}
.file-picker__mask {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
right: 0;
top: 0;
bottom: 0;
left: 0;
color: #fff;
font-size: 14px;
background-color: rgba(0, 0, 0, 0.4);
}
.uni-file-picker__lists-box {
position: relative;
}
.uni-file-picker__item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
padding: 8px 10px;
padding-right: 5px;
padding-left: 10px;
}
.files-border {
border-top: 1px #eee solid;
}
.files__name {
flex: 1;
font-size: 14px;
color: #666;
margin-right: 25px;
/* #ifndef APP-NVUE */
word-break: break-all;
word-wrap: break-word;
/* #endif */
}
.icon-files {
/* #ifndef APP-NVUE */
position: static;
background-color: initial;
/* #endif */
}
// .icon-files .icon-del {
// background-color: #333;
// width: 12px;
// height: 1px;
// }
.is-list-card {
border: 1px #eee solid;
margin-bottom: 5px;
border-radius: 5px;
box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.1);
padding: 5px;
}
.files__image {
width: 40px;
height: 40px;
margin-right: 10px;
}
.header-image {
width: 100%;
height: 100%;
}
.is-text-box {
border: 1px #eee solid;
border-radius: 5px;
}
.is-text-image {
width: 25px;
height: 25px;
margin-left: 5px;
}
.rotate {
position: absolute;
transform: rotate(90deg);
}
.icon-del-box {
/* #ifndef APP-NVUE */
display: flex;
margin: auto 0;
/* #endif */
align-items: center;
justify-content: center;
position: absolute;
top: 0px;
bottom: 0;
right: 5px;
height: 26px;
width: 26px;
// border-radius: 50%;
// background-color: rgba(0, 0, 0, 0.5);
z-index: 2;
transform: rotate(-45deg);
}
.icon-del {
width: 15px;
height: 1px;
background-color: #333;
// border-radius: 1px;
}
/* #ifdef H5 */
@media all and (min-width: 768px) {
.uni-file-picker__files {
max-width: 375px;
}
}
/* #endif */
</style>

View File

@ -1,292 +0,0 @@
<template>
<view class="uni-file-picker__container">
<view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle">
<view class="file-picker__box-content" :style="borderStyle">
<image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image>
<view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)">
<view class="icon-del"></view>
<view class="icon-del rotate"></view>
</view>
<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
:backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
</view>
<view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
点击重试
</view>
</view>
</view>
<view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle">
<view class="file-picker__box-content is-add" :style="borderStyle" @click="choose">
<slot>
<view class="icon-add"></view>
<view class="icon-add rotate"></view>
</slot>
</view>
</view>
</view>
</template>
<script>
export default {
name: "uploadImage",
emits:['uploadFiles','choose','delFile'],
props: {
filesList: {
type: Array,
default () {
return []
}
},
disabled:{
type: Boolean,
default: false
},
disablePreview: {
type: Boolean,
default: false
},
limit: {
type: [Number, String],
default: 9
},
imageStyles: {
type: Object,
default () {
return {
width: 'auto',
height: 'auto',
border: {}
}
}
},
delIcon: {
type: Boolean,
default: true
},
readonly:{
type:Boolean,
default:false
}
},
computed: {
styles() {
let styles = {
width: 'auto',
height: 'auto',
border: {}
}
return Object.assign(styles, this.imageStyles)
},
boxStyle() {
const {
width = 'auto',
height = 'auto'
} = this.styles
let obj = {}
if (height === 'auto') {
if (width !== 'auto') {
obj.height = this.value2px(width)
obj['padding-top'] = 0
} else {
obj.height = 0
}
} else {
obj.height = this.value2px(height)
obj['padding-top'] = 0
}
if (width === 'auto') {
if (height !== 'auto') {
obj.width = this.value2px(height)
} else {
obj.width = '33.3%'
}
} else {
obj.width = this.value2px(width)
}
let classles = ''
for(let i in obj){
classles+= `${i}:${obj[i]};`
}
return classles
},
borderStyle() {
let {
border
} = this.styles
let obj = {}
const widthDefaultValue = 1
const radiusDefaultValue = 3
if (typeof border === 'boolean') {
obj.border = border ? '1px #eee solid' : 'none'
} else {
let width = (border && border.width) || widthDefaultValue
width = this.value2px(width)
let radius = (border && border.radius) || radiusDefaultValue
radius = this.value2px(radius)
obj = {
'border-width': width,
'border-style': (border && border.style) || 'solid',
'border-color': (border && border.color) || '#eee',
'border-radius': radius
}
}
let classles = ''
for(let i in obj){
classles+= `${i}:${obj[i]};`
}
return classles
}
},
methods: {
uploadFiles(item, index) {
this.$emit("uploadFiles", item)
},
choose() {
this.$emit("choose")
},
delFile(index) {
this.$emit('delFile', index)
},
prviewImage(img, index) {
let urls = []
if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){
this.$emit("choose")
}
if(this.disablePreview) return
this.filesList.forEach(i => {
urls.push(i.url)
})
uni.previewImage({
urls: urls,
current: index
});
},
value2px(value) {
if (typeof value === 'number') {
value += 'px'
} else {
if (value.indexOf('%') === -1) {
value = value.indexOf('px') !== -1 ? value : value + 'px'
}
}
return value
}
}
}
</script>
<style lang="scss">
.uni-file-picker__container {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-wrap: wrap;
margin: -5px;
}
.file-picker__box {
position: relative;
// flex: 0 0 33.3%;
width: 33.3%;
height: 0;
padding-top: 33.33%;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
.file-picker__box-content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: 5px;
border: 1px #eee solid;
border-radius: 5px;
overflow: hidden;
}
.file-picker__progress {
position: absolute;
bottom: 0;
left: 0;
right: 0;
/* border: 1px red solid; */
z-index: 2;
}
.file-picker__progress-item {
width: 100%;
}
.file-picker__mask {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
right: 0;
top: 0;
bottom: 0;
left: 0;
color: #fff;
font-size: 12px;
background-color: rgba(0, 0, 0, 0.4);
}
.file-image {
width: 100%;
height: 100%;
}
.is-add {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.icon-add {
width: 50px;
height: 5px;
background-color: #f1f1f1;
border-radius: 2px;
}
.rotate {
position: absolute;
transform: rotate(90deg);
}
.icon-del-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
position: absolute;
top: 3px;
right: 3px;
height: 26px;
width: 26px;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 2;
transform: rotate(-45deg);
}
.icon-del {
width: 15px;
height: 2px;
background-color: #fff;
border-radius: 2px;
}
</style>

View File

@ -1,110 +0,0 @@
/**
* 获取文件名和后缀
* @param {String} name
*/
export const get_file_ext = (name) => {
const last_len = name.lastIndexOf('.')
const len = name.length
return {
name: name.substring(0, last_len),
ext: name.substring(last_len + 1, len)
}
}
/**
* 获取扩展名
* @param {Array} fileExtname
*/
export const get_extname = (fileExtname) => {
if (!Array.isArray(fileExtname)) {
let extname = fileExtname.replace(/(\[|\])/g, '')
return extname.split(',')
} else {
return fileExtname
}
return []
}
/**
* 获取文件和检测是否可选
*/
export const get_files_and_is_max = (res, _extname) => {
let filePaths = []
let files = []
if(!_extname || _extname.length === 0){
return {
filePaths,
files
}
}
res.tempFiles.forEach(v => {
let fileFullName = get_file_ext(v.name)
const extname = fileFullName.ext.toLowerCase()
if (_extname.indexOf(extname) !== -1) {
files.push(v)
filePaths.push(v.path)
}
})
if (files.length !== res.tempFiles.length) {
uni.showToast({
title: `当前选择了${res.tempFiles.length}个文件 ${res.tempFiles.length - files.length} 个文件格式不正确`,
icon: 'none',
duration: 5000
})
}
return {
filePaths,
files
}
}
/**
* 获取图片信息
* @param {Object} filepath
*/
export const get_file_info = (filepath) => {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: filepath,
success(res) {
resolve(res)
},
fail(err) {
reject(err)
}
})
})
}
/**
* 获取封装数据
*/
export const get_file_data = async (files, type = 'image') => {
// 最终需要上传数据库的数据
let fileFullName = get_file_ext(files.name)
const extname = fileFullName.ext.toLowerCase()
let filedata = {
name: files.name,
uuid: files.uuid,
extname: extname || '',
cloudPath: files.cloudPath,
fileType: files.fileType,
thumbTempFilePath: files.thumbTempFilePath,
url: files.path || files.path,
size: files.size, //单位是字节
image: {},
path: files.path,
video: {}
}
if (type === 'image') {
const imageinfo = await get_file_info(files.path)
delete filedata.video
filedata.image.width = imageinfo.width
filedata.image.height = imageinfo.height
filedata.image.location = imageinfo.path
} else {
delete filedata.image
}
return filedata
}

View File

@ -1,84 +0,0 @@
{
"id": "uni-file-picker",
"displayName": "uni-file-picker 文件选择上传",
"version": "1.0.11",
"description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间",
"keywords": [
"uni-ui",
"uniui",
"图片上传",
"文件上传"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"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"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,11 +0,0 @@
## FilePicker 文件选择上传
> **组件名uni-file-picker**
> 代码块: `uFilePicker`
文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-file-picker)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,18 +0,0 @@
## 1.2.12022-05-30
- 新增 stat属性是否开启uni统计功能
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-goods-nav](https://uniapp.dcloud.io/component/uniui/uni-goods-nav)
## 1.1.12021-08-24
- 新增 支持国际化
## 1.1.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.72021-05-12
- 新增 组件示例地址
## 1.0.62021-04-21
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
## 1.0.52021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.0.42021-02-05
- 调整为uni_modules目录规范

View File

@ -1,6 +0,0 @@
{
"uni-goods-nav.options.shop": "shop",
"uni-goods-nav.options.cart": "cart",
"uni-goods-nav.buttonGroup.addToCart": "add to cart",
"uni-goods-nav.buttonGroup.buyNow": "buy now"
}

View File

@ -1,8 +0,0 @@
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
}

View File

@ -1,6 +0,0 @@
{
"uni-goods-nav.options.shop": "店铺",
"uni-goods-nav.options.cart": "购物车",
"uni-goods-nav.buttonGroup.addToCart": "加入购物车",
"uni-goods-nav.buttonGroup.buyNow": "立即购买"
}

View File

@ -1,6 +0,0 @@
{
"uni-goods-nav.options.shop": "店鋪",
"uni-goods-nav.options.cart": "購物車",
"uni-goods-nav.buttonGroup.addToCart": "加入購物車",
"uni-goods-nav.buttonGroup.buyNow": "立即購買"
}

View File

@ -1,229 +0,0 @@
<template>
<view class="uni-goods-nav">
<!-- 底部占位 -->
<view class="uni-tab__seat" />
<view class="uni-tab__cart-box flex">
<view class="flex uni-tab__cart-sub-left">
<view v-for="(item,index) in options" :key="index" class="flex uni-tab__cart-button-left uni-tab__shop-cart" @click="onClick(index,item)">
<view class="uni-tab__icon">
<uni-icons :type="item.icon" size="20" color="#646566"></uni-icons>
<!-- <image class="image" :src="item.icon" mode="widthFix" /> -->
</view>
<text class="uni-tab__text">{{ item.text }}</text>
<view class="flex uni-tab__dot-box">
<text v-if="item.info" :class="{ 'uni-tab__dots': item.info > 9 }" class="uni-tab__dot " :style="{'backgroundColor':item.infoBackgroundColor?item.infoBackgroundColor:'#ff0000',
color:item.infoColor?item.infoColor:'#fff'
}">{{ item.info }}</text>
</view>
</view>
</view>
<view :class="{'uni-tab__right':fill}" class="flex uni-tab__cart-sub-right ">
<view v-for="(item,index) in buttonGroup" :key="index" :style="{background:item.backgroundColor,color:item.color}"
class="flex uni-tab__cart-button-right" @click="buttonClick(index,item)"><text :style="{color:item.color}" class="uni-tab__cart-button-right-text">{{ item.text }}</text></view>
</view>
</view>
</view>
</template>
<script>
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const { t } = initVueI18n(messages)
/**
* GoodsNav 商品导航
* @description 商品加入购物车立即购买等
* @tutorial https://ext.dcloud.net.cn/plugin?id=865
* @property {Array} options 组件参数
* @property {Array} buttonGroup 组件按钮组参数
* @property {Boolean} fill = [true | false] 组件按钮组参数
* @property {Boolean} stat 是否开启统计功能
* @event {Function} click 左侧点击事件
* @event {Function} buttonClick 右侧按钮组点击事件
* @example <uni-goods-nav :fill="true" options="" buttonGroup="buttonGroup" @click="" @buttonClick="" />
*/
export default {
name: 'UniGoodsNav',
emits:['click','buttonClick'],
props: {
options: {
type: Array,
default () {
return [{
icon: 'shop',
text: t("uni-goods-nav.options.shop"),
}, {
icon: 'cart',
text: t("uni-goods-nav.options.cart")
}]
}
},
buttonGroup: {
type: Array,
default () {
return [{
text: t("uni-goods-nav.buttonGroup.addToCart"),
backgroundColor: 'linear-gradient(90deg, #FFCD1E, #FF8A18)',
color: '#fff'
},
{
text: t("uni-goods-nav.buttonGroup.buyNow"),
backgroundColor: 'linear-gradient(90deg, #FE6035, #EF1224)',
color: '#fff'
}
]
}
},
fill: {
type: Boolean,
default: false
},
stat:{
type: Boolean,
default: false
}
},
methods: {
onClick(index, item) {
this.$emit('click', {
index,
content: item,
})
},
buttonClick(index, item) {
if (uni.report && this.stat) {
uni.report(item.text, item.text)
}
this.$emit('buttonClick', {
index,
content: item
})
}
}
}
</script>
<style lang="scss" >
.flex {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-goods-nav {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
}
.uni-tab__cart-box {
flex: 1;
height: 50px;
background-color: #fff;
z-index: 900;
}
.uni-tab__cart-sub-left {
padding: 0 5px;
}
.uni-tab__cart-sub-right {
flex: 1;
}
.uni-tab__right {
margin: 5px 0;
margin-right: 10px;
border-radius: 100px;
overflow: hidden;
}
.uni-tab__cart-button-left {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
// flex: 1;
position: relative;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 0 10px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-tab__icon {
width: 18px;
height: 18px;
}
.image {
width: 18px;
height: 18px;
}
.uni-tab__text {
margin-top: 3px;
font-size: 12px;
color: #646566;
}
.uni-tab__cart-button-right {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
/* #endif */
flex: 1;
justify-content: center;
align-items: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-tab__cart-button-right-text {
font-size: 14px;
color: #fff;
}
.uni-tab__cart-button-right:active {
opacity: 0.7;
}
.uni-tab__dot-box {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
/* #endif */
position: absolute;
right: -2px;
top: 2px;
justify-content: center;
align-items: center;
// width: 0;
// height: 0;
}
.uni-tab__dot {
// width: 30rpx;
// height: 30rpx;
padding: 0 4px;
line-height: 15px;
color: #ffffff;
text-align: center;
font-size: 12px;
background-color: #ff0000;
border-radius: 15px;
}
.uni-tab__dots {
padding: 0 4px;
// width: auto;
border-radius: 15px;
}
</style>

View File

@ -1,88 +0,0 @@
{
"id": "uni-goods-nav",
"displayName": "uni-goods-nav 商品导航",
"version": "1.2.1",
"description": "商品导航组件主要用于电商类应用底部导航,可自定义加入购物车,购买等操作",
"keywords": [
"uni-ui",
"uniui",
"商品导航"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,10 +0,0 @@
## GoodsNav 商品导航
> **组件名uni-goods-nav**
> 代码块: `uGoodsNav`
商品加入购物车,立即购买等。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-goods-nav)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,16 +0,0 @@
## 1.2.22022-05-30
- 新增 stat属性是否开启uni统计功能
## 1.2.12021-11-22
- 修复 vue3中某些scss变量无法找到的问题
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-group](https://uniapp.dcloud.io/component/uniui/uni-group)
## 1.1.72021-11-08
## 1.1.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
- 优化 组件文档
## 1.0.32021-05-12
- 新增 组件示例地址
## 1.0.22021-02-05
- 调整为uni_modules目录规范
- 优化 兼容 nvue 页面

View File

@ -1,134 +0,0 @@
<template>
<view class="uni-group" :class="['uni-group--'+mode ,margin?'group-margin':'']" :style="{marginTop: `${top}px` }">
<slot name="title">
<view v-if="title" class="uni-group__title" :style="{'padding-left':border?'30px':'15px'}">
<text class="uni-group__title-text">{{ title }}</text>
</view>
</slot>
<view class="uni-group__content" :class="{'group-conent-padding':border}">
<slot />
</view>
</view>
</template>
<script>
/**
* Group 分组
* @description 表单字段分组
* @tutorial https://ext.dcloud.net.cn/plugin?id=3281
* @property {String} title 主标题
* @property {Number} top 分组间隔
* @property {Number} mode 模式
*/
export default {
name: 'uniGroup',
emits:['click'],
props: {
title: {
type: String,
default: ''
},
top: {
type: [Number, String],
default: 10
},
mode: {
type: String,
default: 'default'
},
stat:{
type: Boolean,
default: false
}
},
data() {
return {
margin: false,
border: false
}
},
watch: {
title(newVal) {
if (uni.report && this.stat && newVal !== '') {
uni.report('title', newVal)
}
}
},
created() {
this.form = this.getForm()
if (this.form) {
this.margin = true
this.border = this.form.border
}
},
methods: {
/**
* 获取父元素实例
*/
getForm() {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== 'uniForms') {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss" >
.uni-group {
background: #fff;
margin-top: 10px;
// border: 1px red solid;
}
.group-margin {
// margin: 0 -15px;
}
.uni-group__title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding-left: 15px;
height: 40px;
background-color: #eee;
font-weight: normal;
color: #666;
}
.uni-group__content {
padding: 15px;
// padding-bottom: 5px;
// background-color: #FFF;
}
.group-conent-padding {
padding: 0 15px;
}
.uni-group__title-text {
font-size: 14px;
color: #666;
}
.distraction {
flex-direction: row;
align-items: center;
}
.uni-group--card {
margin: 10px;
border-radius: 5px;
overflow: hidden;
box-shadow: 0 0 5px 1px rgba($color: #000000, $alpha: 0.08);
}
</style>

View File

@ -1,87 +0,0 @@
{
"id": "uni-group",
"displayName": "uni-group 分组",
"version": "1.2.2",
"description": "分组组件可用于将组件用于分组,添加间隔,以产生明显的区块",
"keywords": [
"uni-ui",
"uniui",
"group",
"分组",
""
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,9 +0,0 @@
## Group 分组
> **组件名uni-group**
> 代码块: `uGroup`
分组组件可用于将组件分组,添加间隔,以产生明显的区块。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-group)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,17 +0,0 @@
## 1.2.12021-11-22
- 修复 vue3中某些scss变量无法找到的问题
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-indexed-list](https://uniapp.dcloud.io/component/uniui/uni-indexed-list)
## 1.1.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.112021-05-12
- 新增 组件示例地址
## 1.0.102021-04-21
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
## 1.0.92021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.0.82021-02-05
- 调整为uni_modules目录规范
- 新增 支持 PC 端

View File

@ -1,144 +0,0 @@
<template>
<view>
<view v-if="loaded || list.itemIndex < 15" class="uni-indexed-list__title-wrapper">
<text v-if="list.items && list.items.length > 0" class="uni-indexed-list__title">{{ list.key }}</text>
</view>
<view v-if="(loaded || list.itemIndex < 15) && list.items && list.items.length > 0" class="uni-indexed-list__list">
<view v-for="(item, index) in list.items" :key="index" class="uni-indexed-list__item" hover-class="uni-indexed-list__item--hover">
<view class="uni-indexed-list__item-container" @click="onClick(idx, index)">
<view class="uni-indexed-list__item-border" :class="{'uni-indexed-list__item-border--last':index===list.items.length-1}">
<view v-if="showSelect" style="margin-right: 20rpx;">
<uni-icons :type="item.checked ? 'checkbox-filled' : 'circle'" :color="item.checked ? '#007aff' : '#C0C0C0'" size="24" />
</view>
<text class="uni-indexed-list__item-content">{{ item.name }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'UniIndexedList',
emits:['itemClick'],
props: {
loaded: {
type: Boolean,
default: false
},
idx: {
type: Number,
default: 0
},
list: {
type: Object,
default () {
return {}
}
},
showSelect: {
type: Boolean,
default: false
}
},
methods: {
onClick(idx, index) {
this.$emit("itemClick", {
idx,
index
})
}
}
}
</script>
<style lang="scss" scoped>
.uni-indexed-list__list {
background-color: $uni-bg-color;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
border-top-style: solid;
border-top-width: 1px;
border-top-color: #DEDEDE;
}
.uni-indexed-list__item {
font-size: 14px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.uni-indexed-list__item-container {
padding-left: 15px;
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-indexed-list__item-border {
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 50px;
padding: 25px;
padding-left: 0;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #DEDEDE;
}
.uni-indexed-list__item-border--last {
border-bottom-width: 0px;
}
.uni-indexed-list__item-content {
flex: 1;
font-size: 14px;
color: #191919;
}
.uni-indexed-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-indexed-list__title-wrapper {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
background-color: #f7f7f7;
}
.uni-indexed-list__title {
padding: 6px 12px;
line-height: 24px;
font-size: 16px;
font-weight: 500;
}
</style>

View File

@ -1,367 +0,0 @@
<template>
<view class="uni-indexed-list" ref="list" id="list">
<!-- #ifdef APP-NVUE -->
<list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false">
<cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx">
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y>
<view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx">
<!-- #endif -->
<indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect"
@itemClick="onClick"></indexed-list-item>
<!-- #ifndef APP-NVUE -->
</view>
</scroll-view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
</cell>
</list>
<!-- #endif -->
<view class="uni-indexed-list__menu" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
@touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove"
@mouseleave.stop="mouseleave">
<view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu--active' : ''">
<text class="uni-indexed-list__menu-text"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text>
</view>
</view>
<view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
<text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text>
</view>
</view>
</template>
<script>
import indexedListItem from './uni-indexed-list-item.vue'
// #ifdef APP-NVUE
const dom = weex.requireModule('dom');
// #endif
// #ifdef APP-PLUS
function throttle(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function touchMove(e) {
let pageY = e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
// #ifndef APP-NVUE
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #endif
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
this.touchmoveIndex = index
// #endif
}
}
const throttleTouchMove = throttle(touchMove, 40)
// #endif
/**
* IndexedList 索引列表
* @description 用于展示索引列表
* @tutorial https://ext.dcloud.net.cn/plugin?id=375
* @property {Boolean} showSelect = [true|false] 展示模式
* @value true 展示模式
* @value false 选择模式
* @property {Object} options 索引列表需要的数据对象
* @event {Function} click 点击列表事件 返回当前选择项的事件对象
* @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
*/
export default {
name: 'UniIndexedList',
components: {
indexedListItem
},
emits: ['click'],
props: {
options: {
type: Array,
default () {
return []
}
},
showSelect: {
type: Boolean,
default: false
}
},
data() {
return {
lists: [],
winHeight: 0,
itemHeight: 0,
winOffsetY: 0,
touchmove: false,
touchmoveIndex: -1,
scrollViewId: '',
touchmovable: true,
loaded: false,
isPC: false
}
},
watch: {
options: {
handler: function() {
this.setList()
},
deep: true
}
},
mounted() {
// #ifdef H5
this.isPC = this.IsPC()
// #endif
setTimeout(() => {
this.setList()
}, 50)
setTimeout(() => {
this.loaded = true
}, 300);
},
methods: {
setList() {
let index = 0;
this.lists = []
this.options.forEach((value, index) => {
if (value.data.length === 0) {
return
}
let indexBefore = index
let items = value.data.map(item => {
let obj = {}
obj['key'] = value.letter
obj['name'] = item
obj['itemIndex'] = index
index++
obj.checked = item.checked ? item.checked : false
return obj
})
this.lists.push({
title: value.letter,
key: value.letter,
items: items,
itemIndex: indexBefore
})
})
// #ifndef APP-NVUE
uni.createSelectorQuery()
.in(this)
.select('#list')
.boundingClientRect()
.exec(ret => {
this.winOffsetY = ret[0].top
this.winHeight = ret[0].height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['list'], (res) => {
this.winOffsetY = res.size.top
this.winHeight = res.size.height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
},
touchStart(e) {
this.touchmove = true
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
// #endif
}
},
touchMove(e) {
// #ifndef APP-PLUS
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
}
// #endif
// #ifdef APP-PLUS
throttleTouchMove.call(this, e)
// #endif
},
touchEnd() {
this.touchmove = false
// this.touchmoveIndex = -1
},
/**
* 兼容 PC @tian
*/
mousedown(e) {
if (!this.isPC) return
this.touchStart(e)
},
mousemove(e) {
if (!this.isPC) return
this.touchMove(e)
},
mouseleave(e) {
if (!this.isPC) return
this.touchEnd(e)
},
// #ifdef H5
IsPC() {
var userAgentInfo = navigator.userAgent;
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (let v = 0; v < Agents.length - 1; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
},
// #endif
onClick(e) {
let {
idx,
index
} = e
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
let select = []
if (this.showSelect) {
this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
this.lists.forEach((value, idx) => {
value.items.forEach((item, index) => {
if (item.checked) {
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
select.push(obj)
}
})
})
}
this.$emit('click', {
item: obj,
select: select
})
}
}
}
</script>
<style lang="scss" scoped>
.uni-indexed-list {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-indexed-list__scroll {
flex: 1;
}
.uni-indexed-list__menu {
width: 24px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-indexed-list__menu-item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
align-items: center;
justify-content: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-indexed-list__menu-text {
font-size: 12px;
text-align: center;
color: #aaa;
}
.uni-indexed-list__menu--active {
// background-color: rgb(200, 200, 200);
}
.uni-indexed-list__menu--active {}
.uni-indexed-list__menu-text--active {
border-radius: 16px;
width: 16px;
height: 16px;
line-height: 16px;
background-color: #007aff;
color: #fff;
}
.uni-indexed-list__alert-wrapper {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.uni-indexed-list__alert {
width: 80px;
height: 80px;
border-radius: 80px;
text-align: center;
line-height: 80px;
font-size: 35px;
color: #fff;
background-color: rgba(0, 0, 0, 0.5);
}
</style>

View File

@ -1,89 +0,0 @@
{
"id": "uni-indexed-list",
"displayName": "uni-indexed-list 索引列表",
"version": "1.2.1",
"description": "索引列表组件,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景",
"keywords": [
"uni-ui",
"索引列表",
"索引",
"列表"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -1,11 +0,0 @@
## IndexedList 索引列表
> **组件名uni-indexed-list**
> 代码块: `uIndexedList`
用于展示索引列表。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-indexed-list)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

Some files were not shown because too many files have changed in this diff Show More