Commit 90e10a07 by liangjianmin

scm_app 项目初始化

parents
Showing with 4888 additions and 0 deletions
/.idea
/.hbuilderx
/unpackage
/*.hbuilderx
/.DS_Store
/node_modules
-/node_modules
.cursor/
/.qoder
/.windsurf
/*.unpackage
/*.windsurfrules
/.kiro
/.shared
/.agent
/*.log
<script>
import { API, ENV } from '@/util/api.js';
import { compareVersion, showUpdateModal } from '@/util/util.js';
export default {
data() {
return {
localVersion: '', // 本地版本号
onlineVersion: '' // 线上版本号
};
},
onLaunch: function() {
},
onShow: function() {
// 测试环境不检查更新
if (ENV === 'production') {
this.checkAppUpdate();
}
},
onHide: function() {
},
methods: {
/**
* 检查应用更新
* 使用 plus.runtime.getProperty 获取 manifest.json 中的 versionName
*/
checkAppUpdate() {
// #ifdef APP-PLUS
plus.runtime.getProperty(plus.runtime.appid, (info) => {
this.localVersion = info.version || '1.0.0';
this.doCheckUpdate();
});
// #endif
// #ifndef APP-PLUS
this.localVersion = '1.0.0';
this.doCheckUpdate();
// #endif
},
/**
* 执行版本检查
*/
doCheckUpdate() {
this.request(API.appVersionList, 'GET', {}, true).then(res => {
if (res.code === 0) {
this.onlineVersion = res.data.list[0].version;
var downloadUrl = res.data.list[0].download_url || '';
var remark = res.data.list[0].remark || '发现新版本,是否更新?';
console.log(`[版本检查] 本地版本: ${this.localVersion}, 线上版本: ${this.onlineVersion}`);
// 比较版本号
if (!compareVersion(this.localVersion, this.onlineVersion)) {
// 需要更新
showUpdateModal(remark, downloadUrl, this.onlineVersion, () => {
// 更新回调,无需更新本地版本变量,因为版本号来自 APP 配置
});
}
}
}).catch(err => {
console.log('检查更新失败:', err);
});
}
}
};
</script>
<style>
@import './assets/css/font.css';
page {
height: 100%;
background: #f1f4f6;
}
</style>
\ No newline at end of file
@font-face {
font-family: "iconfont";
src: url('~@/static/iconfont.ttf?t=1597739255175') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-biyanjing:before {
content: "\e901";
}
.icon-yanjing:before {
content: "\e8bf";
}
.icon-wushuju:before {
content: "\e609";
}
.icon-24gf-successCircle:before {
content: "\e982";
}
.icon-dagou:before {
content: "\e602";
}
.icon-zujian-duoxuan:before {
content: "\e686";
}
.icon-juxing41:before {
content: "\e79c";
}
.icon-a-juxing13:before {
content: "\e791";
}
.icon-danxuanxuanzhong-weixuanzhong:before {
content: "\e605";
}
.icon-danxuankuangdanxuan-xuanzhong-weixuanzhong:before {
content: "\e601";
}
.icon-a-juxing21:before {
content: "\e794";
}
.icon-a-juxing111:before {
content: "\e78f";
}
.icon-juxing11:before {
content: "\e790";
}
.icon-huopinchaxun:before {
content: "\e95e";
}
.icon-a-10cangkucangchu4:before {
content: "\e7a5";
}
.icon-jinggao31:before {
content: "\e762";
}
.icon-jianshao:before {
content: "\e8c5";
}
.icon-xinzeng-:before {
content: "\e678";
}
.icon-a-10cangkucangchu3:before {
content: "\e7a0";
}
.icon-a-10cangkucangchu1:before {
content: "\e79e";
}
.icon-a-10cangkucangchu2:before {
content: "\e79f";
}
.icon-instant-error:before {
content: "\e63f";
}
.icon-sanjiaoxing3:before {
content: "\e78b";
}
.icon-chakanbeizhu:before {
content: "\e78c";
}
.icon-riqi1:before {
content: "\e78d";
}
.icon-juxing10:before {
content: "\e78e";
}
.icon-weixin:before {
content: "\e600";
}
.icon-wuliu:before {
content: "\e75f";
}
.icon-ruku:before {
content: "\e60f";
}
.icon-cangpeitubiao_chukuhuanhuochuku:before {
content: "\e607";
}
.icon-gongyingshang:before {
content: "\e785";
}
.icon-juxing9:before {
content: "\e784";
}
.icon-juxing8:before {
content: "\e782";
}
.icon-jinggao3:before {
content: "\e783";
}
.icon-Chevron1:before {
content: "\e769";
}
.icon-jinggao1:before {
content: "\e780";
}
.icon-a-juxing12:before {
content: "\e77f";
}
.icon-juxing7:before {
content: "\e77d";
}
.icon-sanjiaoxing2:before {
content: "\e77e";
}
.icon-bianzu:before {
content: "\e77b";
}
.icon-a-Fill3:before {
content: "\e77c";
}
.icon-a-Fill3-copy:before {
content: "\e8c6";
}
.icon-xingzhuangjiehe:before {
content: "\e74f";
}
.icon-juxing6:before {
content: "\e77a";
}
.icon-sanjiaoxing:before {
content: "\e779";
}
.icon-sanjiaoxing1:before {
content: "\e763";
}
.icon-a-riqi11:before {
content: "\e758";
}
.icon-jinggao2:before {
content: "\e75d";
}
.icon-juxing5:before {
content: "\e778";
}
.icon-a-10cangkucangchu:before {
content: "\e774";
}
.icon-juxing4:before {
content: "\e777";
}
.icon-jinggao:before {
content: "\e772";
}
.icon-juxing3:before {
content: "\e773";
}
.icon-a-juxing4:before {
content: "\e775";
}
.icon-a-juxing11:before {
content: "\e776";
}
.icon-shanchu:before {
content: "\e8b6";
}
.icon-riqi:before {
content: "\e770";
}
.icon-a-riqi1:before {
content: "\e771";
}
.icon-juxing2:before {
content: "\e76f";
}
.icon-juxing1:before {
content: "\e76e";
}
.icon-juxing:before {
content: "\e76b";
}
.icon-a-juxing1:before {
content: "\e76d";
}
\ No newline at end of file
.home-index {
min-height: 100vh;
padding-bottom: 158rpx;
background: linear-gradient(180deg, #f1f5f9 0%, #e2e8f0 100%);
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.skeleton-line {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 8rpx;
}
.skeleton-head {
padding: 24rpx 28rpx;
min-height: 140rpx;
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
border-radius: 0 0 24rpx 24rpx;
display: flex;
align-items: center;
.skeleton-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background: linear-gradient(90deg, rgba(255,255,255,0.2) 25%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0.2) 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
margin-right: 20rpx;
}
.skeleton-info {
flex: 1;
.skeleton-name {
width: 160rpx;
height: 32rpx;
margin-bottom: 12rpx;
background: linear-gradient(90deg, rgba(255,255,255,0.2) 25%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0.2) 75%);
background-size: 200% 100%;
}
.skeleton-email {
width: 240rpx;
height: 24rpx;
background: linear-gradient(90deg, rgba(255,255,255,0.15) 25%, rgba(255,255,255,0.3) 50%, rgba(255,255,255,0.15) 75%);
background-size: 200% 100%;
}
}
}
.skeleton-list {
margin-top: 24rpx;
padding: 0 24rpx;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24rpx 16rpx;
.skeleton-item {
display: flex;
flex-direction: column;
align-items: center;
.skeleton-icon {
width: 100%;
height: 128rpx;
background: linear-gradient(90deg, #e2e8f0 25%, #cbd5e1 50%, #e2e8f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 16rpx;
border: 1px solid rgba(226, 232, 240, 0.6);
}
.skeleton-text {
width: 75%;
height: 24rpx;
margin-top: 16rpx;
background: linear-gradient(90deg, #e2e8f0 25%, #cbd5e1 50%, #e2e8f0 75%);
background-size: 200% 100%;
}
}
}
.head {
padding: 24rpx 28rpx;
min-height: 140rpx;
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
border-radius: 0 0 24rpx 24rpx;
box-shadow: 0 6rpx 20rpx rgba(37, 99, 235, 0.3);
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: -40%;
right: -15%;
width: 250rpx;
height: 250rpx;
background: radial-gradient(circle, rgba(255, 255, 255, 0.12) 0%, transparent 60%);
pointer-events: none;
}
.info-bar {
position: relative;
z-index: 1;
.icon-juxing {
font-size: 90rpx;
color: #ffffff;
margin-right: 16rpx;
}
.t1 {
font-size: 32rpx;
color: #ffffff;
font-weight: 600;
letter-spacing: 0.5rpx;
}
.t2 {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.9);
margin-top: 6rpx;
}
}
.setting {
position: relative;
z-index: 1;
padding: 14rpx 18rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 20rpx;
transition: all 0.2s ease;
cursor: pointer;
&:active {
background: rgba(255, 255, 255, 0.35);
transform: scale(0.96);
}
.iconfont {
font-size: 26rpx;
color: #ffffff;
}
.tt {
font-size: 24rpx;
color: #ffffff;
margin-left: 6rpx;
font-weight: 500;
}
}
}
.fast-entry {
position: relative;
background: #ffffff;
padding-top: 24rpx;
&::after {
position: absolute;
bottom: 0px;
left: 0;
right: 0;
width: 100%;
height: 1px;
background-color: #ffffff;
content: '';
}
.tit {
font-size: 24rpx;
color: #1e2021;
font-weight: bold;
margin-bottom: 36rpx;
padding-left: 24rpx;
}
.bar {
flex-wrap: wrap;
.box {
position: relative;
flex: 1;
height: 177rpx;
border-right: 1px solid #e6edf0;
border-bottom: 1px solid #e6edf0;
&:nth-of-type(3n) {
border-right: 1px solid transparent;
}
.num {
position: absolute;
right: 32rpx;
top: 5rpx;
width: 42rpx;
height: 32rpx;
background: #bb3434;
border-radius: 16rpx;
font-size: 20rpx;
color: #ffffff;
}
.iconfont {
font-size: 64rpx;
color: #197adb;
}
.text {
margin-top: 8rpx;
font-size: 22rpx;
color: #404547;
}
}
}
}
.list {
margin-top: 24rpx;
padding: 0 24rpx;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24rpx 16rpx;
.box-li {
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
&:active {
transform: scale(0.96);
}
.box {
position: relative;
width: 100%;
height: 128rpx;
background: linear-gradient(145deg, #ffffff 0%, #f8fafc 100%);
border-radius: 16rpx;
border: 1px solid rgba(226, 232, 240, 0.8);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04),
0 4rpx 16rpx rgba(37, 99, 235, 0.03);
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(37, 99, 235, 0.03) 0%, transparent 60%);
opacity: 0;
transition: opacity 0.25s ease;
pointer-events: none;
}
image {
width: 72rpx;
height: 72rpx;
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.exception-count {
position: absolute;
top: 10rpx;
right: 10rpx;
min-width: 36rpx;
height: 36rpx;
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
border-radius: 18rpx;
font-size: 20rpx;
font-weight: 600;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
padding: 0 10rpx;
box-shadow: 0 4rpx 12rpx rgba(249, 115, 22, 0.35);
animation: badge-pulse 2s infinite;
}
}
.text {
margin-top: 16rpx;
font-size: 24rpx;
font-weight: 500;
color: #1e293b;
text-align: center;
letter-spacing: 0.5rpx;
line-height: 1.3;
transition: color 0.2s ease;
}
&:active .box {
border-color: rgba(37, 99, 235, 0.2);
box-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.02);
&::before {
opacity: 1;
}
}
&:active .box image {
transform: scale(1.08);
}
&:active .text {
color: #2563eb;
}
}
}
@keyframes badge-pulse {
0%, 100% {
transform: scale(1);
box-shadow: 0 4rpx 12rpx rgba(249, 115, 22, 0.35);
}
50% {
transform: scale(1.05);
box-shadow: 0 6rpx 16rpx rgba(249, 115, 22, 0.45);
}
}
}
\ No newline at end of file
.mine-index {
padding-top: 24rpx;
.logged-in {
margin: 0 24rpx;
padding: 0 24rpx;
height: 184rpx;
background: #ffffff;
border-radius: 4rpx;
.uni-img {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
.text {
margin-left: 32rpx;
.mb16 {
margin-bottom: 16rpx;
}
.t1 {
font-size: 28rpx;
color: #1E2021;
}
.t2 {
margin-top: 20rpx;
font-size: 22rpx;
color: #6E767A;
}
}
.iconfont {
font-size: 32rpx;
color: #197ADB;
margin-right: 12rpx;
}
.tt {
font-size: 22rpx;
color: #197ADB;
}
}
.not-logged-in {
margin: 0 24rpx;
height: 184rpx;
background: #ffffff;
border-radius: 4rpx;
padding-left: 24rpx;
.uni-img {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
.text {
margin-left: 32rpx;
.t1 {
font-size: 28rpx;
color: #197adb;
}
.t2 {
font-size: 22rpx;
color: #6e767a;
}
.t3 {
font-size: 22rpx;
color: #404547;
margin-left: 16rpx;
}
}
.iconfont {
font-size: 32rpx;
color: #197ADB;
margin-right: 12rpx;
}
.tt {
font-size: 22rpx;
color: #197adb;
}
}
.list {
margin-top: 24rpx;
padding: 0 24rpx;
.box {
padding: 0 24rpx;
height: 88rpx;
background: #FFFFFF;
border-radius: 4rpx;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0rpx;
}
.iconfont {
color: #9CA8AD;
font-size: 36rpx;
margin-right: 20rpx;
}
.tt {
font-size: 28rpx;
color: #1E2021;
}
.text {
font-size: 22rpx;
color: #6E767A;
}
}
}
.exit {
margin: 64rpx 24rpx 0 24rpx;
height: 88rpx;
background: #197ADB;
border-radius: 4rpx;
font-size: 28rpx;
color: #FFFFFF;
}
}
\ No newline at end of file
page {
background: linear-gradient(160deg, #f0f7ff 0%, #e0edff 50%, #f5f8ff 100%);
min-height: 100vh;
}
.page-auth {
min-height: 100vh;
padding: 100rpx 40rpx 160rpx;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: -150rpx;
right: -100rpx;
width: 400rpx;
height: 400rpx;
background: radial-gradient(circle, rgba(37, 99, 235, 0.12) 0%, transparent 70%);
pointer-events: none;
}
&::after {
content: '';
position: absolute;
bottom: 100rpx;
left: -80rpx;
width: 300rpx;
height: 300rpx;
background: radial-gradient(circle, rgba(59, 130, 246, 0.08) 0%, transparent 60%);
pointer-events: none;
}
.logo {
width: 160rpx;
height: 140rpx;
background: url('https://img.ichunt.com/images/ichunt/minProgram/scmMinProgram/662003b7227315cd1089476b71f3589c.png') no-repeat center;
background-size: contain;
margin-bottom: 24rpx;
filter: drop-shadow(0 6rpx 16rpx rgba(37, 99, 235, 0.2));
}
.title {
font-size: 38rpx;
font-weight: 700;
color: #1e293b;
margin-bottom: 64rpx;
letter-spacing: 3rpx;
position: relative;
&::after {
content: '';
position: absolute;
bottom: -16rpx;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 4rpx;
background: linear-gradient(90deg, #3b82f6, #2563eb);
border-radius: 2rpx;
}
}
.form-box {
width: 100%;
padding: 48rpx 36rpx;
background: #ffffff;
border-radius: 24rpx;
box-shadow: 0 4rpx 24rpx rgba(37, 99, 235, 0.08),
0 12rpx 48rpx rgba(0, 0, 0, 0.04);
.input-box {
position: relative;
padding: 0 24rpx;
width: 100%;
height: 96rpx;
background: #f8fafc;
border-radius: 12rpx;
border: 2rpx solid #e2e8f0;
margin-bottom: 24rpx;
transition: all 0.25s ease;
&:last-of-type {
margin-bottom: 0;
}
.iconfont {
font-size: 38rpx;
color: #94a3b8;
margin-right: 20rpx;
transition: color 0.25s ease;
}
.uni-input {
flex: 1;
font-size: 30rpx;
color: #1e293b;
}
.password-toggle {
font-size: 38rpx;
color: #94a3b8;
margin-left: 20rpx;
cursor: pointer;
transition: all 0.25s ease;
&:active {
color: #3b82f6;
transform: scale(0.9);
}
}
&:focus-within {
background: #ffffff;
border-color: #3b82f6;
box-shadow: 0 0 0 4rpx rgba(59, 130, 246, 0.12);
.iconfont {
color: #3b82f6;
}
}
}
.btn {
width: 100%;
height: 96rpx;
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
margin-top: 40rpx;
letter-spacing: 8rpx;
box-shadow: 0 6rpx 20rpx rgba(37, 99, 235, 0.35);
transition: all 0.2s ease;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent);
transition: left 0.4s ease;
}
&:active {
transform: scale(0.98);
box-shadow: 0 3rpx 12rpx rgba(37, 99, 235, 0.25);
}
&:active::before {
left: 100%;
}
&.loading {
opacity: 0.85;
pointer-events: none;
letter-spacing: 4rpx;
}
.loading-icon {
width: 34rpx;
height: 34rpx;
border: 3rpx solid rgba(255, 255, 255, 0.3);
border-top-color: #ffffff;
border-radius: 50%;
margin-right: 14rpx;
animation: spin 0.7s linear infinite;
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
}
.update-btn-box {
width: 100%;
margin: 28rpx auto 0;
justify-content: flex-end;
.update-btn {
height: 56rpx;
padding: 0 20rpx;
background: transparent;
border: 1rpx solid #cbd5e1;
border-radius: 28rpx;
font-size: 24rpx;
color: #64748b;
position: relative;
cursor: pointer;
transition: all 0.2s ease;
&:active {
background: #f1f5f9;
border-color: #3b82f6;
color: #3b82f6;
}
.red-dot {
position: absolute;
top: -4rpx;
right: -4rpx;
width: 16rpx;
height: 16rpx;
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
border-radius: 50%;
border: 2rpx solid #ffffff;
box-shadow: 0 2rpx 6rpx rgba(249, 115, 22, 0.4);
animation: pulse-dot 2s infinite;
}
}
}
@keyframes pulse-dot {
0%,
100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.15);
opacity: 0.85;
}
}
.copyright {
position: fixed;
bottom: 90rpx;
left: 0;
right: 0;
text-align: center;
font-size: 20rpx;
color: #94a3b8;
letter-spacing: 0.5rpx;
}
.version-bar {
position: fixed;
bottom: 130rpx;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
justify-content: center;
padding: 12rpx 24rpx;
background: rgba(255, 255, 255, 0.9);
border-radius: 32rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
border: 1rpx solid rgba(59, 130, 246, 0.12);
.version-current {
font-size: 24rpx;
color: #94a3b8;
font-weight: 500;
}
.version-arrow {
font-size: 22rpx;
color: #cbd5e1;
margin: 0 12rpx;
}
.version-latest {
font-size: 24rpx;
color: #3b82f6;
font-weight: 600;
}
.version-tag {
font-size: 20rpx;
color: #ffffff;
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
padding: 4rpx 12rpx;
border-radius: 16rpx;
margin-left: 16rpx;
font-weight: 500;
}
}
}
\ No newline at end of file
<template>
<view>
<!-- 单选复选框 -->
<view v-if="type === 'single'" class="check-box-icon single-checkbox" :class="{ 'curr': isChecked }" @click="handleSingleChange"></view>
<!-- 全选复选框 -->
<view v-if="type === 'all'" class="check-box row rowCenter verCenter" @click="handleAllChange">
<view class="check-box-icon" :class="{ 'curr': isAllChecked }"></view>
<text class="text">{{ selectedCount === 0 ? '全选' : selectedCount }}</text>
</view>
</view>
</template>
<script>
export default {
name: 'CheckBoxGroup',
props: {
// 类型:single-单选,all-全选
type: {
type: String,
default: 'single'
},
// 单选时的值
value: {
type: [String, Number],
default: ''
},
// 是否选中(单选时使用)
checked: {
type: Boolean,
default: false
},
// 选中的数组(全选时使用)
selectedList: {
type: Array,
default: () => []
},
// 总数据列表(全选时使用)
totalList: {
type: Array,
default: () => []
},
// 数据中用于比较的字段名
valueKey: {
type: String,
default: 'id'
}
},
computed: {
// 单选是否选中
isChecked() {
if (this.type === 'single') {
return this.checked || this.selectedList.includes(this.value);
}
return false;
},
// 全选是否选中
isAllChecked() {
if (this.type === 'all') {
return this.totalList.length > 0 && this.selectedList.length === this.totalList.length;
}
return false;
},
// 选中数量
selectedCount() {
return this.selectedList.length;
}
},
methods: {
/**
* 处理单选变化
*/
handleSingleChange() {
if (this.type === 'single') {
this.$emit('change', this.value, !this.isChecked);
}
},
/**
* 处理全选变化
*/
handleAllChange() {
if (this.type === 'all') {
var newSelectedList = [];
if (!this.isAllChecked) {
// 当前非全选状态,执行全选
newSelectedList = this.totalList.map(item => item[this.valueKey]);
}
this.$emit('change', newSelectedList);
}
}
}
};
</script>
<style lang="scss">
.check-box-icon {
width: 32rpx;
height: 32rpx;
border: 2rpx solid #d9d9d9;
border-radius: 4rpx;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
transition: all 0.3s;
position: relative;
&::after {
content: '';
width: 10rpx;
height: 16rpx;
border: 3rpx solid #fff;
border-top: 0;
border-left: 0;
transform: rotate(45deg) scale(0);
transition: transform 0.2s;
margin-top: -6rpx;
}
&.curr {
background: linear-gradient(135deg, #1677ff 0%, #0958d9 100%);
border-color: #1677ff;
&::after {
transform: rotate(45deg) scale(1);
}
}
&.single-checkbox {
position: absolute;
right: 18rpx;
top: 17rpx;
z-index: 10;
}
}
.check-box {
width: 128rpx;
height: 75rpx;
background: #ffffff;
.check-box-icon {
position: relative;
}
.text {
margin-left: 10rpx;
font-size: 24rpx;
color: #484b59;
}
}
</style>
\ No newline at end of file
<template>
<view class="custom-checkbox-group row verCenter">
<view
class="custom-checkbox-item row verCenter"
v-for="option in options"
:key="option.value"
@click="toggleOption(option.value)"
>
<view class="checkbox-box" :class="{ checked: selectedValues.includes(option.value) }"></view>
<text class="checkbox-label" :class="{ checked: selectedValues.includes(option.value) }">{{ option.label }}</text>
</view>
</view>
</template>
<script>
export default {
name: 'CustomCheckbox',
props: {
// 选项数组 [{ label: '待入库', value: '1' }]
options: {
type: Array,
default: () => []
},
// 当前选中的值数组
value: {
type: Array,
default: () => []
}
},
computed: {
selectedValues() {
return this.value || [];
}
},
methods: {
/**
* 切换选项选择状态
*/
toggleOption(optionValue) {
var newSelectedValues = [...this.selectedValues];
var index = newSelectedValues.indexOf(optionValue);
if (index > -1) {
newSelectedValues.splice(index, 1);
} else {
newSelectedValues.push(optionValue);
}
// 触发父组件的change事件
this.$emit('change', newSelectedValues);
}
}
};
</script>
<style scoped lang="scss">
.custom-checkbox-group {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8rpx;
}
.custom-checkbox-item {
display: flex;
align-items: center;
cursor: pointer;
padding: 10rpx 9rpx;
border-radius: 8rpx;
transition: background-color 0.2s ease;
&:active {
background-color: rgba(25, 105, 249, 0.08);
}
.checkbox-box {
width: 36rpx;
height: 36rpx;
border: 2rpx solid #d1d5db;
border-radius: 8rpx;
background: #fff;
margin-right: 12rpx;
position: relative;
flex-shrink: 0;
transition: all 0.2s ease;
&::after {
content: '';
position: absolute;
left: 50%;
top: 45%;
width: 8rpx;
height: 14rpx;
border: 3rpx solid transparent;
border-right-color: #fff;
border-bottom-color: #fff;
transform: translate(-50%, -50%) rotate(45deg) scale(0);
transition: transform 0.15s ease;
}
&.checked {
background: #1969f9;
border-color: #1969f9;
&::after {
transform: translate(-50%, -50%) rotate(45deg) scale(1);
}
}
}
.checkbox-label {
font-size: 24rpx;
color: #484b59;
user-select: none;
transition: color 0.2s ease;
&.checked {
color: #1969f9;
font-weight: 500;
}
}
}
</style>
<template>
<view class="custom-radio-group row verCenter">
<view
class="custom-radio-item row verCenter"
v-for="option in options"
:key="option.value"
@click="selectOption(option.value)"
>
<view class="radio-box" :class="{ checked: selectedValue === option.value }"></view>
<text class="radio-label" :class="{ checked: selectedValue === option.value }">{{ option.label }}</text>
</view>
</view>
</template>
<script>
export default {
name: 'CustomRadio',
props: {
// 选项数组 [{ label: '全部', value: '' }]
options: {
type: Array,
default: () => []
},
// 当前选中的值
value: {
type: [String, Number],
default: ''
}
},
computed: {
selectedValue() {
return this.value;
}
},
methods: {
/**
* 选择选项
*/
selectOption(optionValue) {
if (this.selectedValue === optionValue) return;
// 触发父组件的change事件
this.$emit('change', optionValue);
}
}
};
</script>
<style scoped lang="scss">
.custom-radio-group {
display: flex;
align-items: center;
width: 100%;
}
.custom-radio-item {
display: flex;
align-items: center;
cursor: pointer;
padding: 8rpx;
border-radius: 8rpx;
&:active {
transform: scale(0.95);
}
.radio-box {
width: 32rpx;
height: 32rpx;
border: 2rpx solid #d1d5db;
border-radius: 50%;
background: #fff;
margin-right: 12rpx;
position: relative;
flex-shrink: 0;
&::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
width: 16rpx;
height: 16rpx;
background: #fff;
border-radius: 50%;
transform: translate(-50%, -50%) scale(0);
transition: transform 0.2s ease;
}
&.checked {
background: #1969f9;
border-color: #1969f9;
&::after {
transform: translate(-50%, -50%) scale(1);
}
}
}
.radio-label {
font-size: 24rpx;
color: #484b59;
user-select: none;
white-space: nowrap;
&.checked {
color: #1969f9;
}
}
}
</style>
<template>
<view class="time-interval-box row bothSide verCenter">
<view class="time-interval row verCenter bothSide">
<view class="row verCenter">
<text class="iconfont icon-juxing41"></text>
<picker mode="date" @change="handleDateChange(1, $event)">
<input type="text" class="uni-input" :placeholder="startPlaceholder" placeholder-style="color:#919399" :value="startTime" />
</picker>
</view>
<text class="iconfont icon-jinggao2" @click="clearStartTime" v-if="startTime"></text>
</view>
<view class="time-interval row verCenter bothSide">
<view class="row verCenter">
<text class="iconfont icon-juxing41"></text>
<picker mode="date" @change="handleDateChange(2, $event)">
<input type="text" class="uni-input" :placeholder="endPlaceholder" placeholder-style="color:#919399" :value="endTime" />
</picker>
</view>
<text class="iconfont icon-jinggao2" @click="clearEndTime" v-if="endTime"></text>
</view>
</view>
</template>
<script>
export default {
props: {
// 开始时间
startTime: {
type: String,
default: ''
},
// 结束时间
endTime: {
type: String,
default: ''
},
// 开始时间占位符
startPlaceholder: {
type: String,
default: '请选择开始日期'
},
// 结束时间占位符
endPlaceholder: {
type: String,
default: '请选择结束日期'
},
// 是否自动校验时间范围
autoValidate: {
type: Boolean,
default: true
}
},
methods: {
/**
* 处理日期选择变化
* @param {Number} type 1-起始时间 2-结束时间
* @param {Object} event 事件对象
*/
handleDateChange(type, event) {
var selectedDate = event.detail.value;
var newStartTime = this.startTime;
var newEndTime = this.endTime;
if (type === 1) {
newStartTime = selectedDate;
} else if (type === 2) {
newEndTime = selectedDate;
}
// 时间范围校验
if (this.autoValidate && newStartTime && newEndTime && newStartTime > newEndTime) {
uni.showToast({
title: '开始时间不能大于结束时间',
icon: 'none'
});
return;
}
// 触发更新事件
this.$emit('change', {
startTime: newStartTime,
endTime: newEndTime,
dateRange: this.formatDateRange(newStartTime, newEndTime)
});
},
/**
* 清除开始时间
*/
clearStartTime() {
this.$emit('change', {
startTime: '',
endTime: this.endTime,
dateRange: this.formatDateRange('', this.endTime)
});
},
/**
* 清除结束时间
*/
clearEndTime() {
this.$emit('change', {
startTime: this.startTime,
endTime: '',
dateRange: this.formatDateRange(this.startTime, '')
});
},
/**
* 格式化日期范围
* @param {String} start 开始时间
* @param {String} end 结束时间
*/
formatDateRange(start, end) {
if (start && end) {
return `${start} ~ ${end}`;
} else if (start) {
return `${start} ~ ${start}`;
} else if (end) {
return `${end} ~ ${end}`;
} else {
return '';
}
}
}
};
</script>
<style scoped lang="scss">
.time-interval-box {
.time-interval {
width: 48%;
height: 60rpx;
background: #ffffff;
border-radius: 10rpx;
padding-left: 17rpx;
padding-right: 10rpx;
box-sizing: border-box;
.iconfont {
font-size: 32rpx;
color: #919399;
}
.uni-input {
margin-left: 13rpx;
font-size: 24rpx;
}
}
}
</style>
\ No newline at end of file
<template>
<scroll-view
class="scroll-list"
:scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="handleLoadMore"
:refresher-enabled="refresherEnabled"
:refresher-triggered="refreshing"
@refresherrefresh="handleRefresh"
>
<!-- 数据列表插槽,使用 key 强制完整渲染,解决 APP 端首次加载 flex 布局计算问题 -->
<view class="slot-wrapper" :key="slotKey">
<slot></slot>
</view>
<!-- 骨架屏 -->
<view class="skeleton-list" v-if="skeleton">
<view class="skeleton-item" v-for="i in skeletonCount" :key="i">
<slot name="skeleton">
<view class="skeleton-checkbox"></view>
<view class="skeleton-content">
<view class="skeleton-row">
<view class="skeleton-line skeleton-line-short"></view>
<view class="skeleton-line skeleton-line-short"></view>
</view>
<view class="skeleton-row">
<view class="skeleton-line skeleton-line-medium"></view>
<view class="skeleton-line skeleton-line-medium"></view>
</view>
<view class="skeleton-row">
<view class="skeleton-line skeleton-line-long"></view>
</view>
<view class="skeleton-row">
<view class="skeleton-line skeleton-line-short"></view>
<view class="skeleton-line skeleton-line-short"></view>
</view>
</view>
<view class="skeleton-btn"></view>
</slot>
</view>
</view>
<!-- 无数据展示 -->
<view class="scroll-list-empty" v-if="showEmpty">
<text class="iconfont icon-wushuju"></text>
<text class="text">{{ emptyText }}</text>
</view>
<!-- 加载更多 -->
<view class="loading-more" v-if="loadingMore">
<view class="loading-icon"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 数据到底提示 -->
<view class="load-bottom" v-if="showBottom">—— {{ bottomText }} ——</view>
</scroll-view>
</template>
<script>
export default {
name: 'ScrollList',
props: {
// 列表数据
list: {
type: Array,
default: () => []
},
// 骨架屏显示状态
skeleton: {
type: Boolean,
default: false
},
// 下拉刷新状态
refreshing: {
type: Boolean,
default: false
},
// 加载更多状态
loadingMore: {
type: Boolean,
default: false
},
// 数据到底标识
loadBottom: {
type: Boolean,
default: false
},
// 是否还有更多数据
hasMoreData: {
type: Boolean,
default: true
},
// 当前页码
page: {
type: Number,
default: 1
},
// 骨架屏数量
skeletonCount: {
type: Number,
default: 4
},
// 空数据提示文字
emptyText: {
type: String,
default: '查不到当前数据'
},
// 数据到底提示文字
bottomText: {
type: String,
default: '数据已经到底'
},
// 触底距离(单位px)
lowerThreshold: {
type: Number,
default: 100
},
// 是否启用下拉刷新
refresherEnabled: {
type: Boolean,
default: true
}
},
computed: {
// slot 的 key,基于 list 长度和 skeleton 状态生成,强制重新渲染
slotKey() {
return `slot-${this.list.length}-${this.skeleton ? 1 : 0}`;
},
// 显示空数据提示
showEmpty() {
return !this.skeleton && this.list.length === 0 && !this.loadingMore && !this.refreshing;
},
// 显示到底提示:第一页不显示,只有翻页后数据到底才显示
showBottom() {
return this.loadBottom && this.list.length > 0 && !this.loadingMore && this.page > 1;
}
},
methods: {
/**
* 下拉刷新
*/
handleRefresh() {
if (this.refreshing) return;
// #ifdef APP-PLUS
uni.vibrateShort();
// #endif
this.$emit('refresh');
},
/**
* 加载更多
*/
handleLoadMore() {
if (!this.hasMoreData || this.loadingMore || this.loadBottom) return;
this.$emit('loadmore');
}
}
};
</script>
<style scoped lang="scss">
.scroll-list {
flex: 1;
height: 100%;
}
.slot-wrapper {
width: 100%;
}
.skeleton-list {
padding: 0;
.skeleton-item {
display: flex;
flex-wrap: wrap;
position: relative;
padding: 15px 17rpx 18rpx 17rpx;
background: #ffffff;
box-shadow: 0px 3rpx 3rpx 0px rgba(198, 199, 204, 0.3);
border-radius: 10rpx;
margin-bottom: 15rpx;
}
.skeleton-checkbox {
width: 36rpx;
height: 36rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 6rpx;
margin-right: 10rpx;
}
.skeleton-content {
flex: 1;
}
.skeleton-row {
display: flex;
flex-wrap: wrap;
margin-bottom: 12rpx;
}
.skeleton-line {
height: 24rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4rpx;
margin-right: 20rpx;
margin-bottom: 8rpx;
}
.skeleton-line-short {
width: 35%;
}
.skeleton-line-medium {
width: 45%;
}
.skeleton-line-long {
width: 90%;
}
.skeleton-btn {
position: absolute;
right: 17rpx;
bottom: 18rpx;
width: 80rpx;
height: 38rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 8rpx;
}
}
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
.scroll-list-empty {
padding: 100rpx 0;
.iconfont {
font-size: 160rpx;
color: #ccc;
}
.text {
font-size: 20rpx;
color: #484B59;
margin-top: 20rpx;
}
}
.loading-more {
padding: 30rpx 0;
.loading-icon {
width: 36rpx;
height: 36rpx;
border: 4rpx solid #e5e5e5;
border-top-color: #1969f9;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin-right: 16rpx;
}
.loading-text {
font-size: 26rpx;
color: #999;
}
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.load-bottom {
padding: 30rpx 0;
font-size: 24rpx;
color: #999;
}
.scroll-list-empty {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.loading-more {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.load-bottom {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
</style>
<template>
<view class="search-box-container row bothSide verCenter">
<!-- 选择器区域 -->
<view class="picker-wrapper row rowCenter verCenter" v-if="showPicker">
<picker @change="handlePickerChange" :value="currentPickerIndex" :range="pickerOptions">
<view class="picker-content row verCenter">
<view class="picker-text">{{ pickerOptions[currentPickerIndex] }}</view>
<view class="picker-arrow"></view>
</view>
</picker>
</view>
<!-- 搜索输入框区域 -->
<view class="input-wrapper row bothSide verCenter" :class="{ 'full-width': !showPicker }">
<view class="input-content row verCenter">
<text class="search-icon iconfont icon-juxing11" @click="handleSearchIconClick"></text>
<input class="search-input" :placeholder="placeholder" :placeholder-style="placeholderStyle" :focus="inputFocus" v-model="inputValue" @input="handleInput" maxlength="-1" />
</view>
<text class="clear-icon iconfont icon-a-juxing111" @click="handleClearInput" v-if="showClearIcon"></text>
</view>
</view>
</template>
<script>
// #ifdef APP-PLUS
import { openScan } from '@/uni_modules/xtf-scanqrcode';
// #endif
export default {
name: 'SearchBoxWithPicker',
props: {
// 是否显示选择器
showPicker: {
type: Boolean,
default: true
},
// 选择器选项数组
pickerOptions: {
type: Array,
default: () => ['全量搜索']
},
// 选择器默认选中索引
pickerIndex: {
type: Number,
default: 0
},
// 输入框占位符
placeholder: {
type: String,
default: '请输入搜索关键词'
},
// 占位符样式
placeholderStyle: {
type: String,
default: 'color:#919399'
},
// 输入框是否自动获取焦点
focus: {
type: Boolean,
default: false
},
// 输入框默认值
value: {
type: String,
default: ''
},
// 是否显示清空图标
showClear: {
type: Boolean,
default: false
},
// 是否启用扫码功能
showScan: {
type: Boolean,
default: false
}
},
data() {
return {
currentPickerIndex: this.pickerIndex,
inputValue: this.value,
inputFocus: this.focus,
showClearIcon: this.showClear
};
},
watch: {
value(newVal) {
this.inputValue = newVal;
},
focus(newVal) {
this.inputFocus = newVal;
},
showClear(newVal) {
this.showClearIcon = newVal;
},
pickerIndex(newVal) {
this.currentPickerIndex = newVal;
}
},
methods: {
/**
* 选择器变化事件
*/
handlePickerChange(e) {
this.currentPickerIndex = e.detail.value;
this.$emit('picker-change', {
index: this.currentPickerIndex,
value: this.pickerOptions[this.currentPickerIndex]
});
},
/**
* 输入框输入事件
*/
handleInput(e) {
const value = e.detail.value;
this.inputValue = value;
this.showClearIcon = !!value;
this.$emit('input', value);
this.$emit('update:value', value);
},
/**
* 清空输入
*/
handleClearInput() {
this.inputValue = '';
this.showClearIcon = false;
this.$emit('clear');
this.$emit('update:value', '');
// 重新获取焦点
this.inputFocus = false;
setTimeout(() => {
this.inputFocus = true;
}, 200);
},
/**
* 搜索图标点击事件
*/
handleSearchIconClick() {
if (!this.showScan) return;
// #ifdef APP-PLUS
openScan({
scanHintText: '请将条码/二维码对准扫描框',
fullScreenScan: true,
isShowBeep: false,
isShowVibrate: true,
isShowLightController: true,
isShowPhotoAlbum: false,
success: (type, data) => {
if (type === 0) {
this.inputValue = data;
this.showClearIcon = true;
this.$emit('update:value', data);
this.$emit('scan', data);
}
}
});
// #endif
// #ifdef MP-WEIXIN
uni.scanCode({
success: (res) => {
var data = res.result;
this.inputValue = data;
this.showClearIcon = true;
this.$emit('update:value', data);
this.$emit('scan', data);
}
});
// #endif
}
}
};
</script>
<style scoped lang="scss">
.search-box-container {
height: 60rpx;
background: #ffffff;
border-radius: 10rpx;
padding: 0 18rpx;
.picker-wrapper {
width: 160rpx;
height: 100%;
border-right: 1px solid #f0f0f2;
padding: 0 12rpx 0 0;
.picker-text {
font-size: 24rpx;
color: #292b33;
font-weight: 600;
}
.picker-arrow {
width: 0;
height: 0;
border-left: 8rpx solid transparent;
border-right: 8rpx solid transparent;
border-top: 11rpx solid #666;
margin-left: 12rpx;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
transform-origin: center 30%;
&:active {
transform: rotate(180deg) scale(1.1);
border-top-color: #1969f9;
}
}
}
.input-wrapper {
flex: 1;
height: 100%;
margin-left: 2rpx;
&.full-width {
margin-left: 0;
}
.input-content {
flex: 1;
}
.search-icon {
font-size: 30rpx;
color: #919399;
margin-left: 10rpx;
margin-right: 14rpx;
flex-shrink: 0;
}
.search-input {
flex: 1;
font-size: 24rpx;
color: #484b59;
height: 100%;
}
.clear-icon {
font-size: 30rpx;
color: #c6c7cc;
margin-left: 10rpx;
flex-shrink: 0;
}
}
}
</style>
<template>
<uni-popup ref="updatePopup" type="center" :mask-click="false">
<view class="update-modal">
<!-- 头部装饰 -->
<view class="modal-header">
<view class="header-decoration">
<view class="decoration-circle circle-1"></view>
<view class="decoration-circle circle-2"></view>
<view class="decoration-circle circle-3"></view>
</view>
<view class="icon-wrapper">
<uni-icons type="cloud-download" size="48" color="#ffffff"></uni-icons>
</view>
<text class="title">更新提示</text>
</view>
<!-- 内容区域 -->
<view class="modal-body">
<scroll-view scroll-y="true" class="content-scroll">
<view class="content-text">{{ remark }}</view>
</scroll-view>
</view>
<!-- 按钮区域 -->
<view class="modal-footer">
<view class="btn-cancel" @click="handleCancel">
<text>取消更新</text>
</view>
<view class="btn-confirm" @click="handleConfirm">
<text>确定更新</text>
</view>
</view>
</view>
</uni-popup>
</template>
<script>
import { downloadApk } from '@/util/util.js';
export default {
name: 'UpdateModal',
data() {
return {
remark: '',
downloadUrl: '',
onlineVersion: '',
successCallback: null
};
},
methods: {
/**
* 显示更新弹窗
* @param {string} remark 更新说明
* @param {string} downloadUrl 下载地址
* @param {string} onlineVersion 线上版本号
* @param {function} successCallback 成功回调
*/
show(remark, downloadUrl, onlineVersion, successCallback) {
this.remark = remark || '发现新版本,请更新';
this.downloadUrl = downloadUrl;
this.onlineVersion = onlineVersion;
this.successCallback = successCallback;
this.$refs.updatePopup.open();
},
/**
* 隐藏弹窗
*/
hide() {
this.$refs.updatePopup.close();
},
/**
* 确认更新
*/
handleConfirm() {
this.hide();
downloadApk(this.downloadUrl, this.onlineVersion, this.successCallback);
this.$emit('confirm');
},
/**
* 取消更新
*/
handleCancel() {
this.hide();
this.$emit('cancel');
}
}
};
</script>
<style scoped lang="scss">
.update-modal {
width: 600rpx;
background: #ffffff;
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.15);
}
// 头部区域
.modal-header {
position: relative;
background: linear-gradient(135deg, #197adb 0%, #1565c0 50%, #0d47a1 100%);
padding: 48rpx 40rpx 40rpx;
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
.header-decoration {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
}
.decoration-circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
}
.circle-1 {
width: 200rpx;
height: 200rpx;
top: -60rpx;
right: -40rpx;
}
.circle-2 {
width: 120rpx;
height: 120rpx;
top: 40rpx;
left: -30rpx;
}
.circle-3 {
width: 80rpx;
height: 80rpx;
bottom: 20rpx;
right: 60rpx;
}
.icon-wrapper {
position: relative;
z-index: 1;
width: 100rpx;
height: 100rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
}
.title {
position: relative;
z-index: 1;
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
letter-spacing: 2rpx;
}
}
// 内容区域
.modal-body {
padding: 40rpx;
background: #ffffff;
.content-scroll {
max-height: 400rpx;
}
.content-text {
font-size: 28rpx;
color: #4a4a4a;
line-height: 1.8;
text-align: left;
white-space: pre-wrap;
word-break: break-word;
}
}
// 按钮区域
.modal-footer {
display: flex;
padding: 24rpx 32rpx 32rpx;
gap: 20rpx;
border-top: 1rpx solid #f0f0f0;
.btn-cancel,
.btn-confirm {
flex: 1;
height: 76rpx;
border-radius: 38rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
font-weight: 500;
transition: opacity 0.2s ease;
&:active {
opacity: 0.8;
}
}
.btn-cancel {
background: #f7f8fa;
color: #646566;
&:active {
background: #f2f3f5;
}
}
.btn-confirm {
background: #197adb;
color: #ffffff;
&:active {
background: #1565c0;
}
}
}
</style>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"baseUrl": ".",
"paths": {
"@/*": [
"./*"
]
}
},
"vueCompilerOptions": {
"globalTypesPath": "./node_modules/vue/dist/vue.d.ts"
},
"include": [
"./**/*"
]
}
\ No newline at end of file
import App from './App'
import {request} from './util/util.js'
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false;
Vue.prototype.request = request;
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif
\ No newline at end of file
{
"name" : "供应链商务",
"appid" : "__UNI__0A46113",
"description" : "供应链商务",
"versionName" : "1.3.1",
"versionCode" : 1,
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {
"Camera" : {},
"Barcode" : {},
"Bluetooth" : {}
},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
],
"minSdkVersion" : 21
},
/* ios打包配置 */
"ios" : {
"dSYMs" : false
},
/* SDK配置 */
"sdkConfigs" : {
"ad" : {}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "wx47674efb6eef06e1",
"libVersion" : "latest",
"setting" : {
"urlCheck" : false,
"es6" : true,
"postcss" : true,
"minified" : true
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2",
"fallbackLocale" : "zh-Hans",
"locale" : "zh-Hans"
}
{
"name": "sc-wms-app",
"version": "1.2.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sc-wms-app",
"version": "1.2.8",
"dependencies": {
"lodash": "^4.18.1"
}
},
"node_modules/lodash": {
"version": "4.18.1",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
}
}
}
{
"name": "sc-wms-app",
"version": "1.2.8",
"description": "供应链商务",
"scripts": {
"release": "node scripts/release.js",
"dev": "node scripts/dev.js"
},
"dependencies": {
"lodash": "^4.18.1"
}
}
{
"pages": [
{
"path": "pages/mine/login",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/home/index",
"style": {
"navigationBarTitleText": "供应链商务",
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "供应链商务",
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#FFFFFF",
"rpxCalcMaxDeviceWidth": 600,
"rpxCalcBaseDeviceWidth": 500
}
}
\ No newline at end of file
<template>
<view class="page-auth column rowCenter verCenter">
<!-- 更新弹窗 -->
<UpdateModal ref="updateModal" @confirm="onUpdateConfirm" />
<text class="logo"></text>
<text class="title">供应链商务系统</text>
<view class="form-box column rowCenter verCenter">
<view class="input-box row verCenter">
<text class="iconfont icon-riqi"></text>
<input type="text" class="uni-input" placeholder="请输入登录账号" v-model="name" placeholder-style="color: #6E767A;" />
</view>
<view class="input-box row verCenter">
<text class="iconfont icon-a-riqi1"></text>
<input :password="!showPassword" class="uni-input" placeholder="请输入登录密码" v-model="passwd" placeholder-style="color: #6E767A;" />
<text class="iconfont password-toggle" :class="showPassword ? 'icon-yanjing' : 'icon-biyanjing'" @click="togglePassword"></text>
</view>
<button class="btn row rowCenter verCenter" :class="{ loading: isLoading }" :disabled="isLoading" @click="submit()">
<text class="loading-icon" v-if="isLoading"></text>
{{ isLoading ? '登录中' : '登录' }}
</button>
</view>
<view class="update-btn-box row rowCenter">
<view class="update-btn row rowCenter verCenter" @click="checkUpdate()">检查更新
<text class="red-dot" v-if="hasUpdate"></text>
</view>
</view>
<view class="copyright">©{{ currentYear }} 深圳市猎芯供应链有限公司 ALL RIGHTS RESERVED</view>
<view class="version-bar" v-if="onlineVersion && localVersion !== onlineVersion">
<text class="version-current">v{{ localVersion }}</text>
<text class="version-arrow"></text>
<text class="version-latest">v{{ onlineVersion }}</text>
<text class="version-tag">可更新</text>
</view>
</view>
</template>
<script>
import { API } from '@/util/api.js';
import md5 from '@/util/md5.js';
import { compareVersion } from '@/util/util.js';
import UpdateModal from '@/components/UpdateModal.vue';
export default {
components: {
UpdateModal
},
data() {
return {
name: '',
passwd: '',
showPassword: false,
isLoading: false,
currentYear: new Date().getFullYear(), // 动态获取当前年份
localVersion: '', // 本地版本号
onlineVersion: '', // 线上版本号
hasUpdate: false // 是否有更新
};
},
onLoad() {
// 清除之前的登录信息
uni.removeStorageSync('oa_skey');
uni.removeStorageSync('oa_user_id');
// 记住密码功能 - 自动填充上次登录的账号密码
var savedName = uni.getStorageSync('name') || '';
var savedPasswd = uni.getStorageSync('passwd') || '';
if (savedName && savedPasswd) {
this.name = savedName;
this.passwd = savedPasswd;
}
// 获取APP版本号
this.getAppVersion();
// 页面加载时静默检查更新
this.silentCheckUpdate();
},
onShow() {
// 可以根据需要决定是否在页面显示时自动检查更新
},
methods: {
/**
* 获取APP版本号
* 使用 plus.runtime.getProperty 获取 manifest.json 中配置的 versionName
*/
getAppVersion() {
// #ifdef APP-PLUS
plus.runtime.getProperty(plus.runtime.appid, (info) => {
// info.version 对应 manifest.json 中的 versionName
this.localVersion = info.version || '1.0.0';
});
// #endif
// #ifndef APP-PLUS
this.localVersion = '1.0.0';
// #endif
},
/**
* 检查更新
*/
checkUpdate() {
this.request(API.appVersionList, 'GET', {}, true).then(res => {
uni.hideLoading();
if (res.code === 0) {
this.onlineVersion = res.data.list[0].version;
var downloadUrl = res.data.list[0].download_url || '';
var remark = res.data.list[0].remark || '发现新版本,是否更新?';
// 比较版本号
if (!compareVersion(this.localVersion, this.onlineVersion)) {
// 需要更新,使用自定义弹窗
this.hasUpdate = true;
this.$refs.updateModal.show(remark, downloadUrl, this.onlineVersion);
} else {
// 已是最新版本,提示当前版本号
this.hasUpdate = false;
uni.showModal({
title: '版本检查',
content: `当前已是最新版本\n版本号:v${this.localVersion}`,
showCancel: false,
confirmText: '知道了'
});
}
} else {
uni.showToast({
title: '检查更新失败',
icon: 'error'
});
}
}).catch(err => {
uni.hideLoading();
uni.showToast({
title: '网络异常',
icon: 'error'
});
});
},
/**
* 更新确认回调
*/
onUpdateConfirm() {
this.hasUpdate = false;
},
/**
* 静默检查更新(不显示加载和成功提示)
*/
silentCheckUpdate() {
this.request(API.appVersionList, 'GET', {}, true).then(res => {
if (res.code === 0) {
this.onlineVersion = res.data.list[0].version;
// 比较版本号,如果有更新则显示红点
if (!compareVersion(this.localVersion, this.onlineVersion)) {
this.hasUpdate = true;
} else {
this.hasUpdate = false;
}
}
}).catch(err => {
// 静默失败,不显示错误提示
console.log('静默检查更新失败:', err);
});
},
/**
* 切换密码显示/隐藏
*/
togglePassword() {
this.showPassword = !this.showPassword;
},
submit() {
if (this.isLoading) return;
// 验证表单
if (!this.name.trim()) {
uni.showToast({
title: '请输入登录账号',
icon: 'error'
});
return;
}
if (!this.passwd.trim()) {
uni.showToast({
title: '请输入登录密码',
icon: 'error'
});
return;
}
// 保存登录信息
uni.setStorageSync('name', this.name);
uni.setStorageSync('passwd', this.passwd);
// 发送登录请求
this.isLoading = true;
this.request(API.login, 'POST', { name: this.name, passwd: md5.hex_md5_32(this.passwd) }, true).then(res => {
if (res.retcode === 0) {
uni.setStorageSync('oa_skey', res.data.skey);
uni.setStorageSync('oa_user_id', res.data.userId);
// 仅 App 端触发震动反馈,告知用户登录成功
// #ifdef APP-PLUS
uni.vibrateShort();
// #endif
uni.redirectTo({
url: '/pages/home/index'
});
uni.showToast({
title: '登录成功',
icon: 'success'
});
} else {
this.isLoading = false;
uni.showModal({
title: '提示',
content: res.errMsg,
showCancel: false
});
}
}).catch(() => {
this.isLoading = false;
})
}
}
};
</script>
<style lang="scss">
@import '@/assets/css/mine/login.scss';
</style>
\ No newline at end of file
/**
* 开发模式脚本 - 切换回测试环境
* 用法: node scripts/dev.js
*/
var fs = require('fs');
var path = require('path');
var rootDir = path.resolve(__dirname, '..');
var apiPath = path.join(rootDir, 'util/api.js');
var apiContent = fs.readFileSync(apiPath, 'utf8');
var newApiContent = apiContent.replace(
/const ENV = ['"]production['"]/,
"const ENV = 'development'"
);
if (apiContent === newApiContent) {
console.log('⚠️ ENV 已经是 development,跳过');
} else {
fs.writeFileSync(apiPath, newApiContent, 'utf8');
console.log('✅ ENV 已切换为 development');
}
console.log('\n🔧 开发模式已启用!');
/**
* 发布脚本 - 自动切换环境 + 版本号递增
* 用法: node scripts/release.js
*/
var fs = require('fs');
var path = require('path');
var rootDir = path.resolve(__dirname, '..');
var apiPath = path.join(rootDir, 'util/api.js');
var manifestPath = path.join(rootDir, 'manifest.json');
// 版本号递增(patch 位 +1)
function bumpVersion(version) {
var parts = version.split('.');
parts[2] = parseInt(parts[2], 10) + 1;
return parts.join('.');
}
// 1. 切换 api.js 环境为 production
var apiContent = fs.readFileSync(apiPath, 'utf8');
var newApiContent = apiContent.replace(
/const ENV = ['"]development['"]/,
"const ENV = 'production'"
);
if (apiContent === newApiContent) {
console.log('⚠️ ENV 已经是 production,跳过');
} else {
fs.writeFileSync(apiPath, newApiContent, 'utf8');
console.log('✅ ENV 已切换为 production');
}
// 2. 更新 manifest.json 版本号
var manifestContent = fs.readFileSync(manifestPath, 'utf8');
var versionMatch = manifestContent.match(/"versionName"\s*:\s*"(\d+\.\d+\.\d+)"/);
if (!versionMatch) {
console.error('❌ 无法解析 versionName');
process.exit(1);
}
var oldVersion = versionMatch[1];
var newVersion = bumpVersion(oldVersion);
var newManifestContent = manifestContent.replace(
/"versionName"\s*:\s*"[\d.]+"/,
`"versionName" : "${newVersion}"`
);
fs.writeFileSync(manifestPath, newManifestContent, 'utf8');
console.log(`✅ 版本号: ${oldVersion}${newVersion}`);
console.log('\n🚀 发布准备完成!');
No preview for this file type
This diff is collapsed. Click to expand it.
## 2.2.34(2024-04-24)
- 新增 日期点击事件,在点击日期时会触发该事件。
## 2.2.33(2024-04-15)
- 修复 抖音小程序事件传递失效bug
## 2.2.32(2024-02-20)
- 修复 日历的close事件触发异常的bug [详情](https://github.com/dcloudio/uni-ui/issues/844)
## 2.2.31(2024-02-20)
- 修复 h5平台 右边日历的月份默认+1的bug [详情](https://github.com/dcloudio/uni-ui/issues/841)
## 2.2.30(2024-01-31)
- 修复 隐藏“秒”时,在IOS15及以下版本时出现 结束时间在开始时间之前 的bug [详情](https://github.com/dcloudio/uni-ui/issues/788)
## 2.2.29(2024-01-20)
- 新增 show事件,弹窗弹出时触发该事件 [详情](https://github.com/dcloudio/uni-app/issues/4694)
## 2.2.28(2024-01-18)
- 去除 noChange事件,当进行日期范围选择时,若只选了一天,则开始结束日期都为同一天 [详情](https://github.com/dcloudio/uni-ui/issues/815)
## 2.2.27(2024-01-10)
- 优化 增加noChange事件,当进行日期范围选择时,若有空值,则触发该事件 [详情](https://github.com/dcloudio/uni-ui/issues/815)
## 2.2.26(2024-01-08)
- 修复 字节小程序时间选择范围器失效问题 [详情](https://github.com/dcloudio/uni-ui/issues/834)
## 2.2.25(2023-10-18)
- 修复 PC端初次修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
## 2.2.24(2023-06-02)
- 修复 部分情况修改时间,开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146)
- 优化 当前月可以选择上月、下月的日期的Bug
## 2.2.23(2023-05-02)
- 修复 部分情况修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
- 修复 部分平台及设备第一次点击无法显示弹框的Bug
- 修复 ios 日期格式未补零显示及使用异常的Bug [详情](https://ask.dcloud.net.cn/question/162979)
## 2.2.22(2023-03-30)
- 修复 日历 picker 修改年月后,自动选中当月1日的Bug [详情](https://ask.dcloud.net.cn/question/165937)
- 修复 小程序端 低版本 ios NaN的Bug [详情](https://ask.dcloud.net.cn/question/162979)
## 2.2.21(2023-02-20)
- 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362)
## 2.2.20(2023-02-17)
- 优化 值为空依然选中当天问题
- 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间
- 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间
- 优化 字节小程序日期时间范围选择,底部日期换行的Bug
## 2.2.19(2023-02-09)
- 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686)
## 2.2.18(2023-02-08)
- 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684)
- 优化 PC端输入日期格式错误时返回当前日期时间
- 优化 PC端输入日期时间超出 start、end 限制的Bug
- 优化 移动端日期时间范围用法时间展示不完整问题
## 2.2.17(2023-02-04)
- 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679)
- 修复 vue3 time-picker 无法显示绑定时分秒的Bug
## 2.2.16(2023-02-02)
- 修复 字节小程序报错的Bug
## 2.2.15(2023-02-02)
- 修复 某些情况切换月份错误的Bug
## 2.2.14(2023-01-30)
- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033)
## 2.2.13(2023-01-10)
- 修复 多次加载组件造成内存占用的Bug
## 2.2.12(2022-12-01)
- 修复 vue3 下 i18n 国际化初始值不正确的Bug
## 2.2.11(2022-09-19)
- 修复 支付宝小程序样式错乱的Bug [详情](https://github.com/dcloudio/uni-app/issues/3861)
## 2.2.10(2022-09-19)
- 修复 反向选择日期范围,日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false)
## 2.2.9(2022-09-16)
- 可以使用 uni-scss 控制主题色
## 2.2.8(2022-09-08)
- 修复 close事件无效的Bug
## 2.2.7(2022-09-05)
- 修复 移动端 maskClick 无效的Bug [详情](https://ask.dcloud.net.cn/question/140824)
## 2.2.6(2022-06-30)
- 优化 组件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致
## 2.2.5(2022-06-24)
- 修复 日历顶部年月及底部确认未国际化的Bug
## 2.2.4(2022-03-31)
- 修复 Vue3 下动态赋值,单选类型未响应的Bug
## 2.2.3(2022-03-28)
- 修复 Vue3 下动态赋值未响应的Bug
## 2.2.2(2021-12-10)
- 修复 clear-icon 属性在小程序平台不生效的Bug
## 2.2.1(2021-12-10)
- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的Bug
## 2.2.0(2021-11-19)
- 优化 组件UI,并提供设计资源 [详情](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移 [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
## 2.1.5(2021-11-09)
- 新增 提供组件设计资源,组件样式调整
## 2.1.4(2021-09-10)
- 修复 hide-second 在移动端的Bug
- 修复 单选赋默认值时,赋值日期未高亮的Bug
- 修复 赋默认值时,移动端未正确显示时间的Bug
## 2.1.3(2021-09-09)
- 新增 hide-second 属性,支持只使用时分,隐藏秒
## 2.1.2(2021-09-03)
- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
- 优化 调整字号大小,美化日历界面
- 修复 因国际化导致的 placeholder 失效的Bug
## 2.1.1(2021-08-24)
- 新增 支持国际化
- 优化 范围选择器在 pc 端过宽的问题
## 2.1.0(2021-08-09)
- 新增 适配 vue3
## 2.0.19(2021-08-09)
- 新增 支持作为 uni-forms 子组件相关功能
- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的Bug
## 2.0.18(2021-08-05)
- 修复 type 属性动态赋值无效的Bug
- 修复 ‘确认’按钮被 tabbar 遮盖 bug
- 修复 组件未赋值时范围选左、右日历相同的Bug
## 2.0.17(2021-08-04)
- 修复 范围选未正确显示当前值的Bug
- 修复 h5 平台(移动端)报错 'cale' of undefined 的Bug
## 2.0.16(2021-07-21)
- 新增 return-type 属性支持返回 date 日期对象
## 2.0.15(2021-07-14)
- 修复 单选日期类型,初始赋值后不在当前日历的Bug
- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
- 优化 移动端移除显示框的清空按钮,无实际用途
## 2.0.14(2021-07-14)
- 修复 组件赋值为空,界面未更新的Bug
- 修复 start 和 end 不能动态赋值的Bug
- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的Bug
## 2.0.13(2021-07-08)
- 修复 范围选择不能动态赋值的Bug
## 2.0.12(2021-07-08)
- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug
## 2.0.11(2021-07-08)
- 优化 弹出层在超出视窗边缘定位不准确的问题
## 2.0.10(2021-07-08)
- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的Bug
- 优化 弹出层在超出视窗边缘被遮盖的问题
## 2.0.9(2021-07-07)
- 新增 maskClick 事件
- 修复 特殊情况日历 rpx 布局错误的Bug,rpx -> px
- 修复 范围选择时清空返回值不合理的bug,['', ''] -> []
## 2.0.8(2021-07-07)
- 新增 日期时间显示框支持插槽
## 2.0.7(2021-07-01)
- 优化 添加 uni-icons 依赖
## 2.0.6(2021-05-22)
- 修复 图标在小程序上不显示的Bug
- 优化 重命名引用组件,避免潜在组件命名冲突
## 2.0.5(2021-05-20)
- 优化 代码目录扁平化
## 2.0.4(2021-05-12)
- 新增 组件示例地址
## 2.0.3(2021-05-10)
- 修复 ios 下不识别 '-' 日期格式的Bug
- 优化 pc 下弹出层添加边框和阴影
## 2.0.2(2021-05-08)
- 修复 在 admin 中获取弹出层定位错误的bug
## 2.0.1(2021-05-08)
- 修复 type 属性向下兼容,默认值从 date 变更为 datetime
## 2.0.0(2021-04-30)
- 支持日历形式的日期+时间的范围选择
> 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)
## 1.0.6(2021-03-18)
- 新增 hide-second 属性,时间支持仅选择时、分
- 修复 选择跟显示的日期不一样的Bug
- 修复 chang事件触发2次的Bug
- 修复 分、秒 end 范围错误的Bug
- 优化 更好的 nvue 适配
<template>
<view class="uni-calendar-item__weeks-box" :class="{
'uni-calendar-item--disable':weeks.disable,
'uni-calendar-item--before-checked-x':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked-x':weeks.afterMultiple,
}" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)">
<view class="uni-calendar-item__weeks-box-item" :class="{
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover),
'uni-calendar-item--checked-range-text': checkHover,
'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,
}">
<text v-if="selected && weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
</view>
<view :class="{'uni-calendar-item--today': weeks.isToday}"></view>
</view>
</template>
<script>
export default {
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
checkHover: {
type: Boolean,
default: false
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
},
handleMousemove(weeks) {
this.$emit('handleMouse', weeks)
}
}
}
</script>
<style lang="scss" >
$uni-primary: #007aff !default;
.uni-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
margin: 1px 0;
position: relative;
}
.uni-calendar-item__weeks-box-text {
font-size: 14px;
// font-family: Lato-Bold, Lato;
font-weight: bold;
color: darken($color: $uni-primary, $amount: 40%);
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-calendar-item__weeks-box-circle {
position: absolute;
top: 5px;
right: 5px;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: #dd524d;
}
.uni-calendar-item__weeks-box .uni-calendar-item--disable {
cursor: default;
}
.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
color: #D1D1D1;
}
.uni-calendar-item--today {
position: absolute;
top: 10px;
right: 17%;
background-color: #dd524d;
width:6px;
height: 6px;
border-radius: 50%;
}
.uni-calendar-item--extra {
color: #dd524d;
opacity: 0.8;
}
.uni-calendar-item__weeks-box .uni-calendar-item--checked {
background-color: $uni-primary;
border-radius: 50%;
box-sizing: border-box;
border: 3px solid #fff;
}
.uni-calendar-item--checked .uni-calendar-item--checked-text {
color: #fff;
}
.uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
color: #333;
}
.uni-calendar-item--multiple {
background-color: #F6F7FC;
// color: #fff;
}
.uni-calendar-item--multiple .uni-calendar-item--before-checked,
.uni-calendar-item--multiple .uni-calendar-item--after-checked {
background-color: $uni-primary;
border-radius: 50%;
box-sizing: border-box;
border: 3px solid #F6F7FC;
}
.uni-calendar-item--before-checked .uni-calendar-item--checked-text,
.uni-calendar-item--after-checked .uni-calendar-item--checked-text {
color: #fff;
}
.uni-calendar-item--before-checked-x {
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
box-sizing: border-box;
background-color: #F6F7FC;
}
.uni-calendar-item--after-checked-x {
border-top-right-radius: 50px;
border-bottom-right-radius: 50px;
background-color: #F6F7FC;
}
</style>
{
"uni-datetime-picker.selectDate": "select date",
"uni-datetime-picker.selectTime": "select time",
"uni-datetime-picker.selectDateTime": "select date and time",
"uni-datetime-picker.startDate": "start date",
"uni-datetime-picker.endDate": "end date",
"uni-datetime-picker.startTime": "start time",
"uni-datetime-picker.endTime": "end time",
"uni-datetime-picker.ok": "ok",
"uni-datetime-picker.clear": "clear",
"uni-datetime-picker.cancel": "cancel",
"uni-datetime-picker.year": "-",
"uni-datetime-picker.month": "",
"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",
"uni-calender.confirm": "confirm"
}
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
}
{
"uni-datetime-picker.selectDate": "选择日期",
"uni-datetime-picker.selectTime": "选择时间",
"uni-datetime-picker.selectDateTime": "选择日期时间",
"uni-datetime-picker.startDate": "开始日期",
"uni-datetime-picker.endDate": "结束日期",
"uni-datetime-picker.startTime": "开始时间",
"uni-datetime-picker.endTime": "结束时间",
"uni-datetime-picker.ok": "确定",
"uni-datetime-picker.clear": "清除",
"uni-datetime-picker.cancel": "取消",
"uni-datetime-picker.year": "年",
"uni-datetime-picker.month": "月",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六",
"uni-calender.confirm": "确认"
}
\ No newline at end of file
{
"uni-datetime-picker.selectDate": "選擇日期",
"uni-datetime-picker.selectTime": "選擇時間",
"uni-datetime-picker.selectDateTime": "選擇日期時間",
"uni-datetime-picker.startDate": "開始日期",
"uni-datetime-picker.endDate": "結束日期",
"uni-datetime-picker.startTime": "開始时间",
"uni-datetime-picker.endTime": "結束时间",
"uni-datetime-picker.ok": "確定",
"uni-datetime-picker.clear": "清除",
"uni-datetime-picker.cancel": "取消",
"uni-datetime-picker.year": "年",
"uni-datetime-picker.month": "月",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六",
"uni-calender.confirm": "確認"
}
\ No newline at end of file
// #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
\ No newline at end of file
{
"id": "uni-datetime-picker",
"displayName": "uni-datetime-picker 日期选择器",
"version": "2.2.34",
"description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择",
"keywords": [
"uni-datetime-picker",
"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",
"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"
}
}
}
}
}
> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护`
## DatetimePicker 时间选择器
> **组件名:uni-datetime-picker**
> 代码块: `uDatetimePicker`
该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。
若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。
**_点击 picker 默认值规则:_**
- 若设置初始值 value, 会显示在 picker 显示框中
- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
## 2.0.10(2024-06-07)
- 优化 uni-app x 中,size 属性的类型
## 2.0.9(2024-01-12)
fix: 修复图标大小默认值错误的问题
## 2.0.8(2023-12-14)
- 修复 项目未使用 ts 情况下,打包报错的bug
## 2.0.7(2023-12-14)
- 修复 size 属性为 string 时,不加单位导致尺寸异常的bug
## 2.0.6(2023-12-11)
- 优化 兼容老版本icon类型,如 top ,bottom 等
## 2.0.5(2023-12-11)
- 优化 兼容老版本icon类型,如 top ,bottom 等
## 2.0.4(2023-12-06)
- 优化 uni-app x 下示例项目图标排序
## 2.0.3(2023-12-06)
- 修复 nvue下引入组件报错的bug
## 2.0.2(2023-12-05)
-优化 size 属性支持单位
## 2.0.1(2023-12-05)
- 新增 uni-app x 支持定义图标
## 1.3.5(2022-01-24)
- 优化 size 属性可以传入不带单位的字符串数值
## 1.3.4(2022-01-24)
- 优化 size 支持其他单位
## 1.3.3(2022-01-17)
- 修复 nvue 有些图标不显示的bug,兼容老版本图标
## 1.3.2(2021-12-01)
- 优化 示例可复制图标名称
## 1.3.1(2021-11-23)
- 优化 兼容旧组件 type 值
## 1.3.0(2021-11-19)
- 新增 更多图标
- 优化 自定义图标使用方式
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons)
## 1.1.7(2021-11-08)
## 1.2.0(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.5(2021-05-12)
- 新增 组件示例地址
## 1.1.4(2021-02-05)
- 调整为uni_modules目录规范
<template>
<text class="uni-icons" :style="styleObj">
<slot>{{unicode}}</slot>
</text>
</template>
<script>
import { fontData, IconsDataItem } from './uniicons_file'
/**
* Icons 图标
* @description 用于展示 icon 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number,String} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: "uni-icons",
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: [Number, String],
default: 16
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {};
},
computed: {
unicode() : string {
let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
if (codes !== null) {
return codes.unicode
}
return ''
},
iconSize() : string {
const size = this.size
if (typeof size == 'string') {
const reg = /^[0-9]*$/g
return reg.test(size as string) ? '' + size + 'px' : '' + size;
// return '' + this.size
}
return this.getFontSize(size as number)
},
styleObj() : UTSJSONObject {
if (this.fontFamily !== '') {
return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
}
return { color: this.color, fontSize: this.iconSize }
}
},
created() { },
methods: {
/**
* 字体大小
*/
getFontSize(size : number) : string {
return size + 'px';
},
},
}
</script>
<style scoped>
@font-face {
font-family: UniIconsFontFamily;
src: url('./uniicons.ttf');
}
.uni-icons {
font-family: UniIconsFontFamily;
font-size: 18px;
font-style: normal;
color: #333;
}
</style>
<template>
<!-- #ifdef APP-NVUE -->
<text :style="styleObj" class="uni-icons" @click="_onClick">{{unicode}}</text>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<text :style="styleObj" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick">
<slot></slot>
</text>
<!-- #endif -->
</template>
<script>
import { fontData } from './uniicons_file_vue.js';
const getVal = (val) => {
const reg = /^[0-9]*$/g
return (typeof val === 'number' || reg.test(val)) ? val + 'px' : val;
}
// #ifdef APP-NVUE
var domModule = weex.requireModule('dom');
import iconUrl from './uniicons.ttf'
domModule.addRule('fontFace', {
'fontFamily': "uniicons",
'src': "url('" + iconUrl + "')"
});
// #endif
/**
* Icons 图标
* @description 用于展示 icons 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: 'UniIcons',
emits: ['click'],
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: [Number, String],
default: 16
},
customPrefix: {
type: String,
default: ''
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {
icons: fontData
}
},
computed: {
unicode() {
let code = this.icons.find(v => v.font_class === this.type)
if (code) {
return code.unicode
}
return ''
},
iconSize() {
return getVal(this.size)
},
styleObj() {
if (this.fontFamily !== '') {
return `color: ${this.color}; font-size: ${this.iconSize}; font-family: ${this.fontFamily};`
}
return `color: ${this.color}; font-size: ${this.iconSize};`
}
},
methods: {
_onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss">
/* #ifndef APP-NVUE */
@import './uniicons.css';
@font-face {
font-family: uniicons;
src: url('./uniicons.ttf');
}
/* #endif */
.uni-icons {
font-family: uniicons;
text-decoration: none;
text-align: center;
}
</style>
.uniui-cart-filled:before {
content: "\e6d0";
}
.uniui-gift-filled:before {
content: "\e6c4";
}
.uniui-color:before {
content: "\e6cf";
}
.uniui-wallet:before {
content: "\e6b1";
}
.uniui-settings-filled:before {
content: "\e6ce";
}
.uniui-auth-filled:before {
content: "\e6cc";
}
.uniui-shop-filled:before {
content: "\e6cd";
}
.uniui-staff-filled:before {
content: "\e6cb";
}
.uniui-vip-filled:before {
content: "\e6c6";
}
.uniui-plus-filled:before {
content: "\e6c7";
}
.uniui-folder-add-filled:before {
content: "\e6c8";
}
.uniui-color-filled:before {
content: "\e6c9";
}
.uniui-tune-filled:before {
content: "\e6ca";
}
.uniui-calendar-filled:before {
content: "\e6c0";
}
.uniui-notification-filled:before {
content: "\e6c1";
}
.uniui-wallet-filled:before {
content: "\e6c2";
}
.uniui-medal-filled:before {
content: "\e6c3";
}
.uniui-fire-filled:before {
content: "\e6c5";
}
.uniui-refreshempty:before {
content: "\e6bf";
}
.uniui-location-filled:before {
content: "\e6af";
}
.uniui-person-filled:before {
content: "\e69d";
}
.uniui-personadd-filled:before {
content: "\e698";
}
.uniui-arrowthinleft:before {
content: "\e6d2";
}
.uniui-arrowthinup:before {
content: "\e6d3";
}
.uniui-arrowthindown:before {
content: "\e6d4";
}
.uniui-back:before {
content: "\e6b9";
}
.uniui-forward:before {
content: "\e6ba";
}
.uniui-arrow-right:before {
content: "\e6bb";
}
.uniui-arrow-left:before {
content: "\e6bc";
}
.uniui-arrow-up:before {
content: "\e6bd";
}
.uniui-arrow-down:before {
content: "\e6be";
}
.uniui-arrowthinright:before {
content: "\e6d1";
}
.uniui-down:before {
content: "\e6b8";
}
.uniui-bottom:before {
content: "\e6b8";
}
.uniui-arrowright:before {
content: "\e6d5";
}
.uniui-right:before {
content: "\e6b5";
}
.uniui-up:before {
content: "\e6b6";
}
.uniui-top:before {
content: "\e6b6";
}
.uniui-left:before {
content: "\e6b7";
}
.uniui-arrowup:before {
content: "\e6d6";
}
.uniui-eye:before {
content: "\e651";
}
.uniui-eye-filled:before {
content: "\e66a";
}
.uniui-eye-slash:before {
content: "\e6b3";
}
.uniui-eye-slash-filled:before {
content: "\e6b4";
}
.uniui-info-filled:before {
content: "\e649";
}
.uniui-reload:before {
content: "\e6b2";
}
.uniui-micoff-filled:before {
content: "\e6b0";
}
.uniui-map-pin-ellipse:before {
content: "\e6ac";
}
.uniui-map-pin:before {
content: "\e6ad";
}
.uniui-location:before {
content: "\e6ae";
}
.uniui-starhalf:before {
content: "\e683";
}
.uniui-star:before {
content: "\e688";
}
.uniui-star-filled:before {
content: "\e68f";
}
.uniui-calendar:before {
content: "\e6a0";
}
.uniui-fire:before {
content: "\e6a1";
}
.uniui-medal:before {
content: "\e6a2";
}
.uniui-font:before {
content: "\e6a3";
}
.uniui-gift:before {
content: "\e6a4";
}
.uniui-link:before {
content: "\e6a5";
}
.uniui-notification:before {
content: "\e6a6";
}
.uniui-staff:before {
content: "\e6a7";
}
.uniui-vip:before {
content: "\e6a8";
}
.uniui-folder-add:before {
content: "\e6a9";
}
.uniui-tune:before {
content: "\e6aa";
}
.uniui-auth:before {
content: "\e6ab";
}
.uniui-person:before {
content: "\e699";
}
.uniui-email-filled:before {
content: "\e69a";
}
.uniui-phone-filled:before {
content: "\e69b";
}
.uniui-phone:before {
content: "\e69c";
}
.uniui-email:before {
content: "\e69e";
}
.uniui-personadd:before {
content: "\e69f";
}
.uniui-chatboxes-filled:before {
content: "\e692";
}
.uniui-contact:before {
content: "\e693";
}
.uniui-chatbubble-filled:before {
content: "\e694";
}
.uniui-contact-filled:before {
content: "\e695";
}
.uniui-chatboxes:before {
content: "\e696";
}
.uniui-chatbubble:before {
content: "\e697";
}
.uniui-upload-filled:before {
content: "\e68e";
}
.uniui-upload:before {
content: "\e690";
}
.uniui-weixin:before {
content: "\e691";
}
.uniui-compose:before {
content: "\e67f";
}
.uniui-qq:before {
content: "\e680";
}
.uniui-download-filled:before {
content: "\e681";
}
.uniui-pyq:before {
content: "\e682";
}
.uniui-sound:before {
content: "\e684";
}
.uniui-trash-filled:before {
content: "\e685";
}
.uniui-sound-filled:before {
content: "\e686";
}
.uniui-trash:before {
content: "\e687";
}
.uniui-videocam-filled:before {
content: "\e689";
}
.uniui-spinner-cycle:before {
content: "\e68a";
}
.uniui-weibo:before {
content: "\e68b";
}
.uniui-videocam:before {
content: "\e68c";
}
.uniui-download:before {
content: "\e68d";
}
.uniui-help:before {
content: "\e679";
}
.uniui-navigate-filled:before {
content: "\e67a";
}
.uniui-plusempty:before {
content: "\e67b";
}
.uniui-smallcircle:before {
content: "\e67c";
}
.uniui-minus-filled:before {
content: "\e67d";
}
.uniui-micoff:before {
content: "\e67e";
}
.uniui-closeempty:before {
content: "\e66c";
}
.uniui-clear:before {
content: "\e66d";
}
.uniui-navigate:before {
content: "\e66e";
}
.uniui-minus:before {
content: "\e66f";
}
.uniui-image:before {
content: "\e670";
}
.uniui-mic:before {
content: "\e671";
}
.uniui-paperplane:before {
content: "\e672";
}
.uniui-close:before {
content: "\e673";
}
.uniui-help-filled:before {
content: "\e674";
}
.uniui-paperplane-filled:before {
content: "\e675";
}
.uniui-plus:before {
content: "\e676";
}
.uniui-mic-filled:before {
content: "\e677";
}
.uniui-image-filled:before {
content: "\e678";
}
.uniui-locked-filled:before {
content: "\e668";
}
.uniui-info:before {
content: "\e669";
}
.uniui-locked:before {
content: "\e66b";
}
.uniui-camera-filled:before {
content: "\e658";
}
.uniui-chat-filled:before {
content: "\e659";
}
.uniui-camera:before {
content: "\e65a";
}
.uniui-circle:before {
content: "\e65b";
}
.uniui-checkmarkempty:before {
content: "\e65c";
}
.uniui-chat:before {
content: "\e65d";
}
.uniui-circle-filled:before {
content: "\e65e";
}
.uniui-flag:before {
content: "\e65f";
}
.uniui-flag-filled:before {
content: "\e660";
}
.uniui-gear-filled:before {
content: "\e661";
}
.uniui-home:before {
content: "\e662";
}
.uniui-home-filled:before {
content: "\e663";
}
.uniui-gear:before {
content: "\e664";
}
.uniui-smallcircle-filled:before {
content: "\e665";
}
.uniui-map-filled:before {
content: "\e666";
}
.uniui-map:before {
content: "\e667";
}
.uniui-refresh-filled:before {
content: "\e656";
}
.uniui-refresh:before {
content: "\e657";
}
.uniui-cloud-upload:before {
content: "\e645";
}
.uniui-cloud-download-filled:before {
content: "\e646";
}
.uniui-cloud-download:before {
content: "\e647";
}
.uniui-cloud-upload-filled:before {
content: "\e648";
}
.uniui-redo:before {
content: "\e64a";
}
.uniui-images-filled:before {
content: "\e64b";
}
.uniui-undo-filled:before {
content: "\e64c";
}
.uniui-more:before {
content: "\e64d";
}
.uniui-more-filled:before {
content: "\e64e";
}
.uniui-undo:before {
content: "\e64f";
}
.uniui-images:before {
content: "\e650";
}
.uniui-paperclip:before {
content: "\e652";
}
.uniui-settings:before {
content: "\e653";
}
.uniui-search:before {
content: "\e654";
}
.uniui-redo-filled:before {
content: "\e655";
}
.uniui-list:before {
content: "\e644";
}
.uniui-mail-open-filled:before {
content: "\e63a";
}
.uniui-hand-down-filled:before {
content: "\e63c";
}
.uniui-hand-down:before {
content: "\e63d";
}
.uniui-hand-up-filled:before {
content: "\e63e";
}
.uniui-hand-up:before {
content: "\e63f";
}
.uniui-heart-filled:before {
content: "\e641";
}
.uniui-mail-open:before {
content: "\e643";
}
.uniui-heart:before {
content: "\e639";
}
.uniui-loop:before {
content: "\e633";
}
.uniui-pulldown:before {
content: "\e632";
}
.uniui-scan:before {
content: "\e62a";
}
.uniui-bars:before {
content: "\e627";
}
.uniui-checkbox:before {
content: "\e62b";
}
.uniui-checkbox-filled:before {
content: "\e62c";
}
.uniui-shop:before {
content: "\e62f";
}
.uniui-headphones:before {
content: "\e630";
}
.uniui-cart:before {
content: "\e631";
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment